/** * Core PubNub API module. */ // region Imports // region Components import { EventDispatcher, Listener } from './components/event-dispatcher'; import { SubscriptionManager } from './components/subscription-manager'; import NotificationsPayload from './components/push_payload'; import { TokenManager } from './components/token_manager'; import { AbstractRequest } from './components/request'; import Crypto from './components/cryptography/index'; import { encode } from './components/base64_codec'; import uuidGenerator from './components/uuid'; // endregion // region Types import { Payload, ResultCallback, Status, StatusCallback, StatusEvent } from './types/api'; // endregion // region Component Interfaces import { ClientConfiguration, PrivateClientConfiguration } from './interfaces/configuration'; import { Cryptography } from './interfaces/cryptography'; import { Transport } from './interfaces/transport'; // endregion // region Constants import RequestOperation from './constants/operations'; import StatusCategory from './constants/categories'; // endregion import { createValidationError, PubNubError } from '../errors/pubnub-error'; import { PubNubAPIError } from '../errors/pubnub-api-error'; import { RetryPolicy, Endpoint } from './components/retry-policy'; // region Event Engine import { PresenceEventEngine } from '../event-engine/presence/presence'; import { EventEngine } from '../event-engine'; // endregion // region Publish & Signal import * as Publish from './endpoints/publish'; import * as Signal from './endpoints/signal'; // endregion // region Subscription import { SubscribeRequestParameters as SubscribeRequestParameters, SubscribeRequest, PubNubEventType, } from './endpoints/subscribe'; import { ReceiveMessagesSubscribeRequest } from './endpoints/subscriptionUtils/receiveMessages'; import { HandshakeSubscribeRequest } from './endpoints/subscriptionUtils/handshake'; import { Subscription as SubscriptionObject } from '../entities/subscription'; import * as Subscription from './types/api/subscription'; // endregion // region Presence import { GetPresenceStateRequest } from './endpoints/presence/get_state'; import { SetPresenceStateRequest } from './endpoints/presence/set_state'; import { HeartbeatRequest } from './endpoints/presence/heartbeat'; import { PresenceLeaveRequest } from './endpoints/presence/leave'; import { WhereNowRequest } from './endpoints/presence/where_now'; import { HereNowRequest } from './endpoints/presence/here_now'; import * as Presence from './types/api/presence'; // endregion // region Message Storage import { DeleteMessageRequest } from './endpoints/history/delete_messages'; import { MessageCountRequest } from './endpoints/history/message_counts'; import { GetHistoryRequest } from './endpoints/history/get_history'; import { FetchMessagesRequest } from './endpoints/fetch_messages'; import * as History from './types/api/history'; // endregion // region Message Actions import { GetMessageActionsRequest } from './endpoints/actions/get_message_actions'; import { AddMessageActionRequest } from './endpoints/actions/add_message_action'; import { RemoveMessageAction } from './endpoints/actions/remove_message_action'; import * as MessageAction from './types/api/message-action'; // endregion // region File sharing import { PublishFileMessageRequest } from './endpoints/file_upload/publish_file'; import { GetFileDownloadUrlRequest } from './endpoints/file_upload/get_file_url'; import { DeleteFileRequest } from './endpoints/file_upload/delete_file'; import { FilesListRequest } from './endpoints/file_upload/list_files'; import { SendFileRequest } from './endpoints/file_upload/send_file'; import * as FileSharing from './types/api/file-sharing'; import { PubNubFileInterface } from './types/file'; // endregion // region PubNub Access Manager import { RevokeTokenRequest } from './endpoints/access_manager/revoke_token'; import { GrantTokenRequest } from './endpoints/access_manager/grant_token'; import { GrantRequest } from './endpoints/access_manager/grant'; import { AuditRequest } from './endpoints/access_manager/audit'; import * as PAM from './types/api/access-manager'; // endregion // region Entities import { SubscriptionCapable, SubscriptionOptions, SubscriptionType, } from '../entities/interfaces/subscription-capable'; import { EventEmitCapable } from '../entities/interfaces/event-emit-capable'; import { EntityInterface } from '../entities/interfaces/entity-interface'; import { SubscriptionBase } from '../entities/subscription-base'; import { ChannelMetadata } from '../entities/channel-metadata'; import { SubscriptionSet } from '../entities/subscription-set'; import { ChannelGroup } from '../entities/channel-group'; import { UserMetadata } from '../entities/user-metadata'; import { Channel } from '../entities/channel'; // endregion // region Channel Groups import PubNubChannelGroups from './pubnub-channel-groups'; // endregion // region Push Notifications import PubNubPushNotifications from './pubnub-push'; // endregion // region App Context import * as AppContext from './types/api/app-context'; import PubNubObjects from './pubnub-objects'; // endregion // region Time import * as Time from './endpoints/time'; // endregion import { EventHandleCapable } from '../entities/interfaces/event-handle-capable'; import { DownloadFileRequest } from './endpoints/file_upload/download_file'; import { SubscriptionInput } from './types/api/subscription'; import { LoggerManager } from './components/logger-manager'; import { LogLevel as LoggerLogLevel } from './interfaces/logger'; import { encodeString, messageFingerprint } from './utils'; import { Entity } from '../entities/entity'; import Categories from './constants/categories'; // endregion // -------------------------------------------------------- // ------------------------ Types ------------------------- // -------------------------------------------------------- // region Types /** * Core PubNub client configuration object. * * @internal */ type ClientInstanceConfiguration = { /** * Client-provided configuration. */ configuration: PrivateClientConfiguration; /** * Transport provider for requests execution. */ transport: Transport; /** * REST API endpoints access tokens manager. */ tokenManager?: TokenManager; /** * Legacy crypto module implementation. */ cryptography?: Cryptography; /** * Legacy crypto (legacy data encryption / decryption and request signature support). */ crypto?: Crypto; }; // endregion /** * Platform-agnostic PubNub client core. */ export class PubNubCore< CryptographyTypes, FileConstructorParameters, PlatformFile extends Partial = Record, > implements EventEmitCapable { /** * PubNub client configuration. * * @internal */ protected readonly _configuration: PrivateClientConfiguration; /** * Subscription loop manager. * * **Note:** Manager created when EventEngine is off. * * @internal */ private readonly subscriptionManager?: SubscriptionManager; /** * Transport for network requests processing. * * @internal */ protected readonly transport: Transport; /** * `userId` change handler. * * @internal */ protected onUserIdChange?: (userId: string) => void; /** * Heartbeat interval change handler. * * @internal */ protected onHeartbeatIntervalChange?: (interval: number) => void; /** * User's associated presence data change handler. * * @internal */ protected onPresenceStateChange?: (state: Record) => void; /** * `authKey` or `token` change handler. * * @internal */ protected onAuthenticationChange?: (auth?: string) => void; /** * REST API endpoints access tokens manager. * * @internal */ private readonly tokenManager?: TokenManager; /** * Legacy crypto module implementation. * * @internal */ private readonly cryptography?: Cryptography; /** * Legacy crypto (legacy data encryption / decryption and request signature support). * * @internal */ private readonly crypto?: Crypto; /** * User's presence event engine. * * @internal */ private readonly presenceEventEngine?: PresenceEventEngine; /** * List of subscribe capable objects with active subscriptions. * * Track list of {@link Subscription} and {@link SubscriptionSet} objects with active * subscription. * * @internal */ private eventHandleCapable: Record = {}; /** * Client-level subscription set. * * **Note:** client-level subscription set for {@link subscribe}, {@link unsubscribe}, and {@link unsubscribeAll} * backward compatibility. * * **Important:** This should be removed as soon as the legacy subscription loop will be dropped. * * @internal */ private _globalSubscriptionSet?: SubscriptionSet; /** * Subscription event engine. * * @internal */ private readonly eventEngine?: EventEngine; /** * Client-managed presence information. * * @internal */ private readonly presenceState?: Record; /** * Event emitter, which will notify listeners about updates received for channels / groups. * * @internal */ private readonly eventDispatcher?: EventDispatcher; /** * Created entities. * * Map of entities which have been created to access. * * @internal */ private readonly entities: Record = {}; /** * PubNub App Context REST API entry point. * * @internal */ // @ts-expect-error Allowed to simplify interface when module can be disabled. private readonly _objects: PubNubObjects; /** * PubNub Channel Group REST API entry point. * * @internal */ // @ts-expect-error Allowed to simplify interface when module can be disabled. private readonly _channelGroups: PubNubChannelGroups; /** * PubNub Push Notification REST API entry point. * * @internal */ // @ts-expect-error Allowed to simplify interface when module can be disabled. private readonly _push: PubNubPushNotifications; /** * {@link ArrayBuffer} to {@link string} decoder. * * @internal */ private static decoder = new TextDecoder(); // -------------------------------------------------------- // ----------------------- Static ------------------------- // -------------------------------------------------------- // region Static /** * Type of REST API endpoint which reported status. */ static OPERATIONS = RequestOperation; /** * API call status category. */ static CATEGORIES = StatusCategory; /** * Enum with API endpoint groups which can be used with retry policy to set up exclusions (which shouldn't be * retried). */ static Endpoint = Endpoint; /** * Exponential retry policy constructor. */ static ExponentialRetryPolicy = RetryPolicy.ExponentialRetryPolicy; /** * Linear retry policy constructor. */ static LinearRetryPolicy = RetryPolicy.LinearRetryPolicy; /** * Disabled / inactive retry policy. * * **Note:** By default `ExponentialRetryPolicy` is set for subscribe requests and this one can be used to disable * retry policy for all requests (setting `undefined` for retry configuration will set default policy). */ static NoneRetryPolicy = RetryPolicy.None; /** * Available minimum log levels. */ static LogLevel = LoggerLogLevel; /** * Construct notification payload which will trigger push notification. * * @param title - Title which will be shown on notification. * @param body - Payload which will be sent as part of notification. * * @returns Pre-formatted message payload which will trigger push notification. */ static notificationPayload(title: string, body: string) { if (process.env.PUBLISH_MODULE !== 'disabled') { return new NotificationsPayload(title, body); } else throw new Error('Notification Payload error: publish module disabled'); } /** * Generate unique identifier. * * @returns Unique identifier. */ static generateUUID() { return uuidGenerator.createUUID(); } // endregion /** * Create and configure PubNub client core. * * @param configuration - PubNub client core configuration. * @returns Configured and ready to use PubNub client. * * @internal */ constructor(configuration: ClientInstanceConfiguration) { this._configuration = configuration.configuration; this.cryptography = configuration.cryptography; this.tokenManager = configuration.tokenManager; this.transport = configuration.transport; this.crypto = configuration.crypto; this.logger.debug('PubNub', () => ({ messageType: 'object', message: configuration.configuration as unknown as Record, details: 'Create with configuration:', ignoredKeys(key: string, obj: Record) { return typeof obj[key] === 'function' || key.startsWith('_'); }, })); // API group entry points initialization. if (process.env.APP_CONTEXT_MODULE !== 'disabled') this._objects = new PubNubObjects(this._configuration, this.sendRequest.bind(this)); if (process.env.CHANNEL_GROUPS_MODULE !== 'disabled') this._channelGroups = new PubNubChannelGroups( this._configuration.logger(), this._configuration.keySet, this.sendRequest.bind(this), ); if (process.env.MOBILE_PUSH_MODULE !== 'disabled') this._push = new PubNubPushNotifications( this._configuration.logger(), this._configuration.keySet, this.sendRequest.bind(this), ); if (process.env.SUBSCRIBE_MODULE !== 'disabled') { // Prepare for a real-time events announcement. this.eventDispatcher = new EventDispatcher(); if (this._configuration.enableEventEngine) { if (process.env.SUBSCRIBE_EVENT_ENGINE_MODULE !== 'disabled') { this.logger.debug('PubNub', 'Using new subscription loop management.'); let heartbeatInterval = this._configuration.getHeartbeatInterval(); this.presenceState = {}; if (process.env.PRESENCE_MODULE !== 'disabled') { if (heartbeatInterval) { this.presenceEventEngine = new PresenceEventEngine({ heartbeat: (parameters, callback) => { this.logger.trace('PresenceEventEngine', () => ({ messageType: 'object', message: { ...parameters }, details: 'Heartbeat with parameters:', })); return this.heartbeat(parameters, callback); }, leave: (parameters) => { this.logger.trace('PresenceEventEngine', () => ({ messageType: 'object', message: { ...parameters }, details: 'Leave with parameters:', })); this.makeUnsubscribe(parameters, () => {}); }, heartbeatDelay: () => new Promise((resolve, reject) => { heartbeatInterval = this._configuration.getHeartbeatInterval(); if (!heartbeatInterval) reject(new PubNubError('Heartbeat interval has been reset.')); else setTimeout(resolve, heartbeatInterval * 1000); }), emitStatus: (status) => this.emitStatus(status), config: this._configuration, presenceState: this.presenceState, }); } } this.eventEngine = new EventEngine({ handshake: (parameters) => { this.logger.trace('EventEngine', () => ({ messageType: 'object', message: { ...parameters }, details: 'Handshake with parameters:', ignoredKeys: ['abortSignal', 'crypto', 'timeout', 'keySet', 'getFileUrl'], })); return this.subscribeHandshake(parameters); }, receiveMessages: (parameters) => { this.logger.trace('EventEngine', () => ({ messageType: 'object', message: { ...parameters }, details: 'Receive messages with parameters:', ignoredKeys: ['abortSignal', 'crypto', 'timeout', 'keySet', 'getFileUrl'], })); return this.subscribeReceiveMessages(parameters); }, delay: (amount) => new Promise((resolve) => setTimeout(resolve, amount)), join: (parameters) => { this.logger.trace('EventEngine', () => ({ messageType: 'object', message: { ...parameters }, details: 'Join with parameters:', })); if (parameters && (parameters.channels ?? []).length === 0 && (parameters.groups ?? []).length === 0) { this.logger.trace('EventEngine', "Ignoring 'join' announcement request."); return; } this.join(parameters); }, leave: (parameters) => { this.logger.trace('EventEngine', () => ({ messageType: 'object', message: { ...parameters }, details: 'Leave with parameters:', })); if (parameters && (parameters.channels ?? []).length === 0 && (parameters.groups ?? []).length === 0) { this.logger.trace('EventEngine', "Ignoring 'leave' announcement request."); return; } this.leave(parameters); }, leaveAll: (parameters) => { this.logger.trace('EventEngine', () => ({ messageType: 'object', message: { ...parameters }, details: 'Leave all with parameters:', })); this.leaveAll(parameters); }, presenceReconnect: (parameters) => { this.logger.trace('EventEngine', () => ({ messageType: 'object', message: { ...parameters }, details: 'Reconnect with parameters:', })); this.presenceReconnect(parameters); }, presenceDisconnect: (parameters) => { this.logger.trace('EventEngine', () => ({ messageType: 'object', message: { ...parameters }, details: 'Disconnect with parameters:', })); this.presenceDisconnect(parameters); }, presenceState: this.presenceState, config: this._configuration, emitMessages: (cursor, events) => { try { this.logger.debug('EventEngine', () => { const hashedEvents = events.map((event) => { const pn_mfp = event.type === PubNubEventType.Message || event.type === PubNubEventType.Signal ? messageFingerprint(event.data.message) : undefined; return pn_mfp ? { type: event.type, data: { ...event.data, pn_mfp } } : event; }); return { messageType: 'object', message: hashedEvents, details: 'Received events:' }; }); events.forEach((event) => this.emitEvent(cursor, event)); } catch (e) { const errorStatus: Status = { error: true, category: StatusCategory.PNUnknownCategory, errorData: e as Error, statusCode: 0, }; this.emitStatus(errorStatus); } }, emitStatus: (status) => this.emitStatus(status), }); } else throw new Error('Event Engine error: subscription event engine module disabled'); } else { if (process.env.SUBSCRIBE_MANAGER_MODULE !== 'disabled') { this.logger.debug('PubNub', 'Using legacy subscription loop management.'); this.subscriptionManager = new SubscriptionManager( this._configuration, (cursor, event) => { try { this.emitEvent(cursor, event); } catch (e) { const errorStatus: Status = { error: true, category: StatusCategory.PNUnknownCategory, errorData: e as Error, statusCode: 0, }; this.emitStatus(errorStatus); } }, this.emitStatus.bind(this), (parameters, callback) => { this.logger.trace('SubscriptionManager', () => ({ messageType: 'object', message: { ...parameters }, details: 'Subscribe with parameters:', ignoredKeys: ['crypto', 'timeout', 'keySet', 'getFileUrl'], })); this.makeSubscribe(parameters, callback); }, (parameters, callback) => { this.logger.trace('SubscriptionManager', () => ({ messageType: 'object', message: { ...parameters }, details: 'Heartbeat with parameters:', ignoredKeys: ['crypto', 'timeout', 'keySet', 'getFileUrl'], })); return this.heartbeat(parameters, callback); }, (parameters, callback) => { this.logger.trace('SubscriptionManager', () => ({ messageType: 'object', message: { ...parameters }, details: 'Leave with parameters:', })); this.makeUnsubscribe(parameters, callback); }, this.time.bind(this), ); } else throw new Error('Subscription Manager error: subscription manager module disabled'); } } } // -------------------------------------------------------- // -------------------- Configuration ---------------------- // -------------------------------------------------------- // region Configuration /** * PubNub client configuration. * * @returns Currently user PubNub client configuration. */ public get configuration(): ClientConfiguration { return this._configuration; } /** * Current PubNub client configuration. * * @returns Currently user PubNub client configuration. * * @deprecated Use {@link configuration} getter instead. */ public get _config(): ClientConfiguration { return this.configuration; } /** * REST API endpoint access authorization key. * * It is required to have `authorization key` with required permissions to access REST API * endpoints when `PAM` enabled for user key set. */ get authKey(): string | undefined { return this._configuration.authKey ?? undefined; } /** * REST API endpoint access authorization key. * * It is required to have `authorization key` with required permissions to access REST API * endpoints when `PAM` enabled for user key set. */ getAuthKey(): string | undefined { return this.authKey; } /** * Change REST API endpoint access authorization key. * * @param authKey - New authorization key which should be used with new requests. */ setAuthKey(authKey: string): void { this.logger.debug('PubNub', `Set auth key: ${authKey}`); this._configuration.setAuthKey(authKey); if (this.onAuthenticationChange) this.onAuthenticationChange(authKey); } /** * Get a PubNub client user identifier. * * @returns Current PubNub client user identifier. */ get userId(): string { return this._configuration.userId!; } /** * Change the current PubNub client user identifier. * * **Important:** Change won't affect ongoing REST API calls. * **Warning:** Because ongoing REST API calls won't be canceled there could happen unexpected events like implicit * `join` event for the previous `userId` after a long-poll subscribe request will receive a response. To avoid this * it is advised to unsubscribe from all/disconnect before changing `userId`. * * @param value - New PubNub client user identifier. * * @throws Error empty user identifier has been provided. */ set userId(value: string) { if (!value || typeof value !== 'string' || value.trim().length === 0) { const error = new Error('Missing or invalid userId parameter. Provide a valid string userId'); this.logger.error('PubNub', () => ({ messageType: 'error', message: error })); throw error; } this.logger.debug('PubNub', `Set user ID: ${value}`); this._configuration.userId = value; if (this.onUserIdChange) this.onUserIdChange(this._configuration.userId); } /** * Get a PubNub client user identifier. * * @returns Current PubNub client user identifier. */ getUserId(): string { return this._configuration.userId!; } /** * Change the current PubNub client user identifier. * * **Important:** Change won't affect ongoing REST API calls. * * @param value - New PubNub client user identifier. * * @throws Error empty user identifier has been provided. */ setUserId(value: string): void { this.userId = value; } /** * Real-time updates filtering expression. * * @returns Filtering expression. */ get filterExpression(): string | undefined { return this._configuration.getFilterExpression() ?? undefined; } /** * Real-time updates filtering expression. * * @returns Filtering expression. */ getFilterExpression(): string | undefined { return this.filterExpression; } /** * Update real-time updates filtering expression. * * @param expression - New expression which should be used or `undefined` to disable filtering. */ set filterExpression(expression: string | null | undefined) { this.logger.debug('PubNub', `Set filter expression: ${expression}`); this._configuration.setFilterExpression(expression); } /** * Update real-time updates filtering expression. * * @param expression - New expression which should be used or `undefined` to disable filtering. */ setFilterExpression(expression: string | null): void { this.logger.debug('PubNub', `Set filter expression: ${expression}`); this.filterExpression = expression; } /** * Dta encryption / decryption key. * * @returns Currently used key for data encryption / decryption. */ get cipherKey(): string | undefined { return this._configuration.getCipherKey(); } /** * Change data encryption / decryption key. * * @param key - New key which should be used for data encryption / decryption. */ set cipherKey(key: string | undefined) { this._configuration.setCipherKey(key); } /** * Change data encryption / decryption key. * * @param key - New key which should be used for data encryption / decryption. */ setCipherKey(key: string): void { this.logger.debug('PubNub', `Set cipher key: ${key}`); this.cipherKey = key; } /** * Change a heartbeat requests interval. * * @param interval - New presence request heartbeat intervals. */ set heartbeatInterval(interval: number) { this.logger.debug('PubNub', `Set heartbeat interval: ${interval}`); this._configuration.setHeartbeatInterval(interval); if (this.onHeartbeatIntervalChange) this.onHeartbeatIntervalChange(this._configuration.getHeartbeatInterval() ?? 0); } /** * Change a heartbeat requests interval. * * @param interval - New presence request heartbeat intervals. */ setHeartbeatInterval(interval: number): void { this.heartbeatInterval = interval; } /** * Get registered loggers' manager. * * @returns Registered loggers' manager. */ get logger(): LoggerManager { return this._configuration.logger(); } /** * Get PubNub SDK version. * * @returns Current SDK version. */ getVersion(): string { return this._configuration.getVersion(); } /** * Add framework's prefix. * * @param name - Name of the framework which would want to add own data into `pnsdk` suffix. * @param suffix - Suffix with information about a framework. */ _addPnsdkSuffix(name: string, suffix: string | number) { this.logger.debug('PubNub', `Add '${name}' 'pnsdk' suffix: ${suffix}`); this._configuration._addPnsdkSuffix(name, suffix); } // -------------------------------------------------------- // ---------------------- Deprecated ---------------------- // -------------------------------------------------------- // region Deprecated /** * Get a PubNub client user identifier. * * @returns Current PubNub client user identifier. * * @deprecated Use the {@link getUserId} or {@link userId} getter instead. */ getUUID(): string { return this.userId; } /** * Change the current PubNub client user identifier. * * **Important:** Change won't affect ongoing REST API calls. * * @param value - New PubNub client user identifier. * * @throws Error empty user identifier has been provided. * * @deprecated Use the {@link PubNubCore#setUserId setUserId} or {@link PubNubCore#userId userId} setter instead. */ setUUID(value: string) { this.logger.warn('PubNub', "'setUserId` is deprecated, please use 'setUserId' or 'userId' setter instead."); this.logger.debug('PubNub', `Set UUID: ${value}`); this.userId = value; } /** * Custom data encryption method. * * @deprecated Instead use {@link cryptoModule} for data encryption. */ get customEncrypt(): ((data: string) => string) | undefined { return this._configuration.getCustomEncrypt(); } /** * Custom data decryption method. * * @deprecated Instead use {@link cryptoModule} for data decryption. */ get customDecrypt(): ((data: string) => string) | undefined { return this._configuration.getCustomDecrypt(); } // endregion // endregion // -------------------------------------------------------- // ---------------------- Entities ------------------------ // -------------------------------------------------------- // region Entities /** * Create a `Channel` entity. * * Entity can be used for the interaction with the following API: * - `subscribe` * * @param name - Unique channel name. * @returns `Channel` entity. */ public channel(name: string): Channel { let channel = this.entities[`${name}_ch`]; if (!channel) channel = this.entities[`${name}_ch`] = new Channel(name, this); return channel as Channel; } /** * Create a `ChannelGroup` entity. * * Entity can be used for the interaction with the following API: * - `subscribe` * * @param name - Unique channel group name. * @returns `ChannelGroup` entity. */ public channelGroup(name: string): ChannelGroup { let channelGroup = this.entities[`${name}_chg`]; if (!channelGroup) channelGroup = this.entities[`${name}_chg`] = new ChannelGroup(name, this); return channelGroup as ChannelGroup; } /** * Create a `ChannelMetadata` entity. * * Entity can be used for the interaction with the following API: * - `subscribe` * * @param id - Unique channel metadata object identifier. * @returns `ChannelMetadata` entity. */ public channelMetadata(id: string): ChannelMetadata { let metadata = this.entities[`${id}_chm`]; if (!metadata) metadata = this.entities[`${id}_chm`] = new ChannelMetadata(id, this); return metadata as ChannelMetadata; } /** * Create a `UserMetadata` entity. * * Entity can be used for the interaction with the following API: * - `subscribe` * * @param id - Unique user metadata object identifier. * @returns `UserMetadata` entity. */ public userMetadata(id: string): UserMetadata { let metadata = this.entities[`${id}_um`]; if (!metadata) metadata = this.entities[`${id}_um`] = new UserMetadata(id, this); return metadata as UserMetadata; } /** * Create subscriptions set object. * * @param parameters - Subscriptions set configuration parameters. */ public subscriptionSet(parameters: { channels?: string[]; channelGroups?: string[]; subscriptionOptions?: SubscriptionOptions; }): SubscriptionSet { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { // Prepare a list of entities for a set. const entities: (EntityInterface & SubscriptionCapable)[] = []; parameters.channels?.forEach((name) => entities.push(this.channel(name))); parameters.channelGroups?.forEach((name) => entities.push(this.channelGroup(name))); return new SubscriptionSet({ client: this, entities, options: parameters.subscriptionOptions }); } else throw new Error('Subscription set error: subscription module disabled'); } // endregion // -------------------------------------------------------- // ----------------------- Common ------------------------- // -------------------------------------------------------- // region Common /** * Schedule request execution. * * @param request - REST API request. * @param callback - Request completion handler callback. * * @returns Asynchronous request execution and response parsing result. * * @internal */ private sendRequest( request: AbstractRequest, callback: ResultCallback, ): void; /** * Schedule request execution. * * @internal * * @param request - REST API request. * * @returns Asynchronous request execution and response parsing result. */ private async sendRequest( request: AbstractRequest, ): Promise; /** * Schedule request execution. * * @internal * * @param request - REST API request. * @param [callback] - Request completion handler callback. * * @returns Asynchronous request execution and response parsing result or `void` in case if * `callback` provided. * * @throws PubNubError in case of request processing error. */ private async sendRequest( request: AbstractRequest, callback?: ResultCallback, ): Promise { // Validate user-input. const validationResult = request.validate(); if (validationResult) { const validationError = createValidationError(validationResult); this.logger.error('PubNub', () => ({ messageType: 'error', message: validationError })); if (callback) return callback(validationError, null); throw new PubNubError('Validation failed, check status for details', validationError); } // Complete request configuration. const transportRequest = request.request(); const operation = request.operation(); if ( (transportRequest.formData && transportRequest.formData.length > 0) || operation === RequestOperation.PNDownloadFileOperation ) { // Set file upload / download request delay. transportRequest.timeout = this._configuration.getFileTimeout(); } else { if ( operation === RequestOperation.PNSubscribeOperation || operation === RequestOperation.PNReceiveMessagesOperation ) transportRequest.timeout = this._configuration.getSubscribeTimeout(); else transportRequest.timeout = this._configuration.getTransactionTimeout(); } // API request processing status. const status: Status = { error: false, operation, category: StatusCategory.PNAcknowledgmentCategory, statusCode: 0, }; const [sendableRequest, cancellationController] = this.transport.makeSendable(transportRequest); /** * **Important:** Because of multiple environments where JS SDK can be used, control over * cancellation had to be inverted to let the transport provider solve a request cancellation task * more efficiently. As a result, cancellation controller can be retrieved and used only after * the request will be scheduled by the transport provider. */ request.cancellationController = cancellationController ? cancellationController : null; return sendableRequest .then((response) => { status.statusCode = response.status; // Handle a special case when request completed but not fully processed by PubNub service. if (response.status !== 200 && response.status !== 204) { const responseText = PubNubCore.decoder.decode(response.body); const contentType = response.headers['content-type']; if (contentType || contentType.indexOf('javascript') !== -1 || contentType.indexOf('json') !== -1) { const json = JSON.parse(responseText) as Payload; if (typeof json === 'object' && 'error' in json && json.error && typeof json.error === 'object') status.errorData = json.error; } else status.responseText = responseText; } return request.parse(response); }) .then((parsed) => { // Notify callback (if possible). if (callback) return callback(status, parsed); return parsed; }) .catch((error: Error) => { const apiError = !(error instanceof PubNubAPIError) ? PubNubAPIError.create(error) : error; // Notify callback (if possible). if (callback) { if (apiError.category !== Categories.PNCancelledCategory) { this.logger.error('PubNub', () => ({ messageType: 'error', message: apiError.toPubNubError(operation, 'REST API request processing error, check status for details'), })); } return callback(apiError.toStatus(operation), null); } const pubNubError = apiError.toPubNubError( operation, 'REST API request processing error, check status for details', ); if (apiError.category !== Categories.PNCancelledCategory) this.logger.error('PubNub', () => ({ messageType: 'error', message: pubNubError })); throw pubNubError; }); } /** * Unsubscribe from all channels and groups. * * @param [isOffline] - Whether `offline` presence should be notified or not. */ public destroy(isOffline: boolean = false): void { this.logger.info('PubNub', 'Destroying PubNub client.'); if (process.env.SUBSCRIBE_MODULE !== 'disabled') { if (this._globalSubscriptionSet) { this._globalSubscriptionSet.invalidate(true); this._globalSubscriptionSet = undefined; } Object.values(this.eventHandleCapable).forEach((subscription) => subscription.invalidate(true)); this.eventHandleCapable = {}; if (this.subscriptionManager) { this.subscriptionManager.unsubscribeAll(isOffline); this.subscriptionManager.disconnect(); } else if (this.eventEngine) this.eventEngine.unsubscribeAll(isOffline); } if (process.env.PRESENCE_MODULE !== 'disabled') { if (this.presenceEventEngine) this.presenceEventEngine.leaveAll(isOffline); } } /** * Unsubscribe from all channels and groups. * * @deprecated Use {@link destroy} method instead. */ public stop(): void { this.logger.warn('PubNub', "'stop' is deprecated, please use 'destroy' instead."); this.destroy(); } // endregion // -------------------------------------------------------- // ---------------------- Publish API --------------------- // -------------------------------------------------------- // region Publish API /** * Publish data to a specific channel. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ public publish(parameters: Publish.PublishParameters, callback: ResultCallback): void; /** * Publish data to a specific channel. * * @param parameters - Request configuration parameters. * * @returns Asynchronous publish data response. */ public async publish(parameters: Publish.PublishParameters): Promise; /** * Publish data to a specific channel. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous publish data response or `void` in case if `callback` provided. */ async publish( parameters: Publish.PublishParameters, callback?: ResultCallback, ): Promise { if (process.env.PUBLISH_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Publish with parameters:', })); const isFireRequest = parameters.replicate === false && parameters.storeInHistory === false; const request = new Publish.PublishRequest({ ...parameters, keySet: this._configuration.keySet, crypto: this._configuration.getCryptoModule(), }); const logResponse = (response: Publish.PublishResponse | null) => { if (!response) return; this.logger.debug( 'PubNub', `${isFireRequest ? 'Fire' : 'Publish'} success with timetoken: ${response.timetoken}`, ); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Publish error: publish module disabled'); } // endregion // -------------------------------------------------------- // ---------------------- Signal API ---------------------- // -------------------------------------------------------- // region Signal API /** * Signal data to a specific channel. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ public signal(parameters: Signal.SignalParameters, callback: ResultCallback): void; /** * Signal data to a specific channel. * * @param parameters - Request configuration parameters. * * @returns Asynchronous signal data response. */ public async signal(parameters: Signal.SignalParameters): Promise; /** * Signal data to a specific channel. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous signal data response or `void` in case if `callback` provided. */ async signal( parameters: Signal.SignalParameters, callback?: ResultCallback, ): Promise { if (process.env.PUBLISH_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Signal with parameters:', })); const request = new Signal.SignalRequest({ ...parameters, keySet: this._configuration.keySet, }); const logResponse = (response: Signal.SignalResponse | null) => { if (!response) return; this.logger.debug('PubNub', `Publish success with timetoken: ${response.timetoken}`); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Publish error: publish module disabled'); } // endregion // -------------------------------------------------------- // ----------------------- Fire API ---------------------- // -------------------------------------------------------- // region Fire API /** * `Fire` a data to a specific channel. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. * * @deprecated Use {@link publish} method instead. */ public fire(parameters: Publish.PublishParameters, callback: ResultCallback): void; /** * `Fire` a data to a specific channel. * * @param parameters - Request configuration parameters. * * @returns Asynchronous signal data response. * * @deprecated Use {@link publish} method instead. */ public async fire(parameters: Publish.PublishParameters): Promise; /** * `Fire` a data to a specific channel. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous signal data response or `void` in case if `callback` provided. * * @deprecated Use {@link publish} method instead. */ async fire( parameters: Publish.PublishParameters, callback?: ResultCallback, ): Promise { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Fire with parameters:', })); callback ??= () => {}; return this.publish({ ...parameters, replicate: false, storeInHistory: false }, callback); } // endregion // -------------------------------------------------------- // -------------------- Subscribe API --------------------- // -------------------------------------------------------- // region Subscribe API /** * Global subscription set which supports legacy subscription interface. * * @returns Global subscription set. * * @internal */ private get globalSubscriptionSet() { if (!this._globalSubscriptionSet) this._globalSubscriptionSet = this.subscriptionSet({}); return this._globalSubscriptionSet; } /** * Subscription-based current timetoken. * * @returns Timetoken based on current timetoken plus diff between current and loop start time. * * @internal */ get subscriptionTimetoken(): string | undefined { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { if (this.subscriptionManager) return this.subscriptionManager.subscriptionTimetoken; else if (this.eventEngine) return this.eventEngine.subscriptionTimetoken; } return undefined; } /** * Get list of channels on which PubNub client currently subscribed. * * @returns List of active channels. */ public getSubscribedChannels(): string[] { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { if (this.subscriptionManager) return this.subscriptionManager.subscribedChannels; else if (this.eventEngine) return this.eventEngine.getSubscribedChannels(); } else throw new Error('Subscription error: subscription module disabled'); return []; } /** * Get list of channel groups on which PubNub client currently subscribed. * * @returns List of active channel groups. */ public getSubscribedChannelGroups(): string[] { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { if (this.subscriptionManager) return this.subscriptionManager.subscribedChannelGroups; else if (this.eventEngine) return this.eventEngine.getSubscribedChannelGroups(); } else throw new Error('Subscription error: subscription module disabled'); return []; } /** * Register an events handler object ({@link Subscription} or {@link SubscriptionSet}) with an active subscription. * * @param subscription - {@link Subscription} or {@link SubscriptionSet} object. * @param [cursor] - Subscription catchup timetoken. * @param [subscriptions] - List of subscriptions for partial subscription loop update. * * @internal */ public registerEventHandleCapable( subscription: SubscriptionBase, cursor?: Subscription.SubscriptionCursor, subscriptions?: EventHandleCapable[], ) { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { this.logger.trace('PubNub', () => ({ messageType: 'object', message: { subscription: subscription, ...(cursor ? { cursor } : []), ...(subscriptions ? { subscriptions } : {}), }, details: `Register event handle capable:`, })); if (!this.eventHandleCapable[subscription.state.id]) this.eventHandleCapable[subscription.state.id] = subscription; let subscriptionInput: SubscriptionInput; if (!subscriptions || subscriptions.length === 0) subscriptionInput = subscription.subscriptionInput(false); else { subscriptionInput = new SubscriptionInput({}); subscriptions.forEach((subscription) => subscriptionInput.add(subscription.subscriptionInput(false))); } const parameters: Subscription.SubscribeParameters = {}; parameters.channels = subscriptionInput.channels; parameters.channelGroups = subscriptionInput.channelGroups; if (cursor) parameters.timetoken = cursor.timetoken; if (this.subscriptionManager) this.subscriptionManager.subscribe(parameters); else if (this.eventEngine) this.eventEngine.subscribe(parameters); } } /** * Unregister an events handler object ({@link Subscription} or {@link SubscriptionSet}) with inactive subscription. * * @param subscription - {@link Subscription} or {@link SubscriptionSet} object. * @param [subscriptions] - List of subscriptions for partial subscription loop update. * * @internal */ public unregisterEventHandleCapable(subscription: SubscriptionBase, subscriptions?: SubscriptionObject[]) { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { if (!this.eventHandleCapable[subscription.state.id]) return; const inUseSubscriptions: SubscriptionBase[] = []; this.logger.trace('PubNub', () => ({ messageType: 'object', message: { subscription: subscription, subscriptions }, details: `Unregister event handle capable:`, })); // Check whether only subscription object has been passed to be unregistered. let shouldDeleteEventHandler = !subscriptions || subscriptions.length === 0; // Check whether subscription set is unregistering with all managed Subscription objects, if ( !shouldDeleteEventHandler && subscription instanceof SubscriptionSet && subscription.subscriptions.length === subscriptions?.length ) shouldDeleteEventHandler = subscription.subscriptions.every((sub) => subscriptions.includes(sub)); if (shouldDeleteEventHandler) delete this.eventHandleCapable[subscription.state.id]; let subscriptionInput: SubscriptionInput; if (!subscriptions || subscriptions.length === 0) { subscriptionInput = subscription.subscriptionInput(true); if (subscriptionInput.isEmpty) inUseSubscriptions.push(subscription); } else { subscriptionInput = new SubscriptionInput({}); subscriptions.forEach((subscription) => { const input = subscription.subscriptionInput(true); if (input.isEmpty) inUseSubscriptions.push(subscription); else subscriptionInput.add(input); }); } if (inUseSubscriptions.length > 0) { this.logger.trace('PubNub', () => { const entities: Entity[] = []; if (inUseSubscriptions[0] instanceof SubscriptionSet) { inUseSubscriptions[0].subscriptions.forEach((subscription) => entities.push(subscription.state.entity as Entity), ); } else inUseSubscriptions.forEach((subscription) => entities.push((subscription as SubscriptionObject).state.entity as Entity), ); return { messageType: 'object', message: { entities }, details: `Can't unregister event handle capable because entities still in use:`, }; }); } if (subscriptionInput.isEmpty) return; else { const _channelGroupsInUse: string[] = []; const _channelsInUse: string[] = []; Object.values(this.eventHandleCapable).forEach((_subscription) => { const _subscriptionInput = _subscription.subscriptionInput(false); const _subscriptionChannelGroups = _subscriptionInput.channelGroups; const _subscriptionChannels = _subscriptionInput.channels; _channelGroupsInUse.push( ...subscriptionInput.channelGroups.filter((channel) => _subscriptionChannelGroups.includes(channel)), ); _channelsInUse.push( ...subscriptionInput.channels.filter((channel) => _subscriptionChannels.includes(channel)), ); }); if (_channelsInUse.length > 0 || _channelGroupsInUse.length > 0) { this.logger.trace('PubNub', () => { const _entitiesInUse: Entity[] = []; const addEntityIfInUse = (entity: Entity) => { const namesOrIds = entity.subscriptionNames(true); const checkList = entity.subscriptionType === SubscriptionType.Channel ? _channelsInUse : _channelGroupsInUse; if (namesOrIds.some((id) => checkList.includes(id))) _entitiesInUse.push(entity); }; Object.values(this.eventHandleCapable).forEach((_subscription) => { if (_subscription instanceof SubscriptionSet) { _subscription.subscriptions.forEach((_subscriptionInSet) => { addEntityIfInUse(_subscriptionInSet.state.entity as Entity); }); } else if (_subscription instanceof SubscriptionObject) addEntityIfInUse(_subscription.state.entity as Entity); }); let details = 'Some entities still in use:'; if (_channelsInUse.length + _channelGroupsInUse.length === subscriptionInput.length) details = "Can't unregister event handle capable because entities still in use:"; return { messageType: 'object', message: { entities: _entitiesInUse }, details }; }); subscriptionInput.remove( new SubscriptionInput({ channels: _channelsInUse, channelGroups: _channelGroupsInUse }), ); if (subscriptionInput.isEmpty) return; } } const parameters: Presence.PresenceLeaveParameters = {}; parameters.channels = subscriptionInput.channels; parameters.channelGroups = subscriptionInput.channelGroups; if (this.subscriptionManager) this.subscriptionManager.unsubscribe(parameters); else if (this.eventEngine) this.eventEngine.unsubscribe(parameters); } } /** * Subscribe to specified channels and groups real-time events. * * @param parameters - Request configuration parameters. */ public subscribe(parameters: Subscription.SubscribeParameters): void { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Subscribe with parameters:', })); // The addition of a new subscription set into the subscribed global subscription set will update the active // subscription loop with new channels and groups. const subscriptionSet = this.subscriptionSet({ ...parameters, subscriptionOptions: { receivePresenceEvents: parameters.withPresence }, }); this.globalSubscriptionSet.addSubscriptionSet(subscriptionSet); subscriptionSet.dispose(); const timetoken = typeof parameters.timetoken === 'number' ? `${parameters.timetoken}` : parameters.timetoken; this.globalSubscriptionSet.subscribe({ timetoken }); } else throw new Error('Subscription error: subscription module disabled'); } /** * Perform subscribe request. * * **Note:** Method passed into managers to let them use it when required. * * @internal * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ private makeSubscribe( parameters: Omit, callback: ResultCallback, ): void { if (process.env.SUBSCRIBE_MANAGER_MODULE !== 'disabled') { // `on-demand` query parameter not required for non-SharedWorker environment. if (!this._configuration.isSharedWorkerEnabled()) parameters.onDemand = false; const request = new SubscribeRequest({ ...parameters, keySet: this._configuration.keySet, crypto: this._configuration.getCryptoModule(), getFileUrl: this.getFileUrl.bind(this), }); this.sendRequest(request, (status, result) => { if (this.subscriptionManager && this.subscriptionManager.abort?.identifier === request.requestIdentifier) this.subscriptionManager.abort = null; callback(status, result); }); /** * Allow subscription cancellation. * * **Note:** Had to be done after scheduling because the transport provider returns the cancellation * controller only when schedule new request. */ if (this.subscriptionManager) { // Creating an identifiable abort caller. const callableAbort = () => request.abort('Cancel long-poll subscribe request'); callableAbort.identifier = request.requestIdentifier; this.subscriptionManager.abort = callableAbort; } } else throw new Error('Subscription error: subscription manager module disabled'); } /** * Unsubscribe from specified channels and groups real-time events. * * @param parameters - Request configuration parameters. */ public unsubscribe(parameters: Presence.PresenceLeaveParameters): void { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Unsubscribe with parameters:', })); if (!this._globalSubscriptionSet) { this.logger.debug('PubNub', 'There are no active subscriptions. Ignore.'); return; } const subscriptions = this.globalSubscriptionSet.subscriptions.filter((subscription) => { const subscriptionInput = subscription.subscriptionInput(false); if (subscriptionInput.isEmpty) return false; for (const channel of parameters.channels ?? []) if (subscriptionInput.contains(channel)) return true; for (const group of parameters.channelGroups ?? []) if (subscriptionInput.contains(group)) return true; }); // Removal from the active subscription also will cause `unsubscribe`. if (subscriptions.length > 0) this.globalSubscriptionSet.removeSubscriptions(subscriptions); } else throw new Error('Unsubscription error: subscription module disabled'); } /** * Perform unsubscribe request. * * **Note:** Method passed into managers to let them use it when required. * * @internal * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ private makeUnsubscribe(parameters: Presence.PresenceLeaveParameters, callback: StatusCallback): void { if (process.env.PRESENCE_MODULE !== 'disabled') { // Filtering out presence channels and groups. let { channels, channelGroups } = parameters; // Remove `-pnpres` channels / groups if they not acceptable in the current PubNub client configuration. if (!this._configuration.getKeepPresenceChannelsInPresenceRequests()) { if (channelGroups) channelGroups = channelGroups.filter((channelGroup) => !channelGroup.endsWith('-pnpres')); if (channels) channels = channels.filter((channel) => !channel.endsWith('-pnpres')); } // Complete immediately request only for presence channels. if ((channelGroups ?? []).length === 0 && (channels ?? []).length === 0) { return callback({ error: false, operation: RequestOperation.PNUnsubscribeOperation, category: StatusCategory.PNAcknowledgmentCategory, statusCode: 200, }); } this.sendRequest( new PresenceLeaveRequest({ channels, channelGroups, keySet: this._configuration.keySet }), callback, ); } else throw new Error('Unsubscription error: presence module disabled'); } /** * Unsubscribe from all channels and groups. */ public unsubscribeAll() { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { this.logger.debug('PubNub', 'Unsubscribe all channels and groups'); // Keeping a subscription set instance after invalidation so to make it possible to deliver the expected // disconnection status. if (this._globalSubscriptionSet) this._globalSubscriptionSet.invalidate(false); Object.values(this.eventHandleCapable).forEach((subscription) => subscription.invalidate(false)); this.eventHandleCapable = {}; if (this.subscriptionManager) this.subscriptionManager.unsubscribeAll(); else if (this.eventEngine) this.eventEngine.unsubscribeAll(); } else throw new Error('Unsubscription error: subscription module disabled'); } /** * Temporarily disconnect from the real-time events stream. * * **Note:** `isOffline` is set to `true` only when a client experiences network issues. * * @param [isOffline] - Whether `offline` presence should be notified or not. */ public disconnect(isOffline: boolean = false): void { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { this.logger.debug('PubNub', `Disconnect (while offline? ${!!isOffline ? 'yes' : 'no'})`); if (this.subscriptionManager) this.subscriptionManager.disconnect(); else if (this.eventEngine) this.eventEngine.disconnect(isOffline); } else throw new Error('Disconnection error: subscription module disabled'); } /** * Restore connection to the real-time events stream. * * @param parameters - Reconnection catch-up configuration. **Note:** available only with the enabled event engine. */ public reconnect(parameters?: { timetoken?: string; region?: number }): void { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Reconnect with parameters:', })); if (this.subscriptionManager) this.subscriptionManager.reconnect(); else if (this.eventEngine) this.eventEngine.reconnect(parameters ?? {}); } else throw new Error('Reconnection error: subscription module disabled'); } /** * Event engine handshake subscribe. * * @internal * * @param parameters - Request configuration parameters. */ private async subscribeHandshake(parameters: Subscription.CancelableSubscribeParameters) { if (process.env.SUBSCRIBE_EVENT_ENGINE_MODULE !== 'disabled') { // `on-demand` query parameter not required for non-SharedWorker environment. if (!this._configuration.isSharedWorkerEnabled()) parameters.onDemand = false; const request = new HandshakeSubscribeRequest({ ...parameters, keySet: this._configuration.keySet, crypto: this._configuration.getCryptoModule(), getFileUrl: this.getFileUrl.bind(this), }); const abortUnsubscribe = parameters.abortSignal.subscribe((err) => { request.abort('Cancel subscribe handshake request'); }); /** * Allow subscription cancellation. * * **Note:** Had to be done after scheduling because the transport provider returns the cancellation * controller only when schedule new request. */ const handshakeResponse = this.sendRequest(request); return handshakeResponse.then((response) => { abortUnsubscribe(); return response.cursor; }); } else throw new Error('Handshake subscription error: subscription event engine module disabled'); } /** * Event engine receive messages subscribe. * * @internal * * @param parameters - Request configuration parameters. */ private async subscribeReceiveMessages(parameters: Subscription.CancelableSubscribeParameters) { if (process.env.SUBSCRIBE_EVENT_ENGINE_MODULE !== 'disabled') { // `on-demand` query parameter not required for non-SharedWorker environment. if (!this._configuration.isSharedWorkerEnabled()) parameters.onDemand = false; const request = new ReceiveMessagesSubscribeRequest({ ...parameters, keySet: this._configuration.keySet, crypto: this._configuration.getCryptoModule(), getFileUrl: this.getFileUrl.bind(this), }); const abortUnsubscribe = parameters.abortSignal.subscribe((err) => { request.abort('Cancel long-poll subscribe request'); }); /** * Allow subscription cancellation. * * **Note:** Had to be done after scheduling because the transport provider returns the cancellation * controller only when schedule new request. */ const receiveResponse = this.sendRequest(request); return receiveResponse.then((response) => { abortUnsubscribe(); return response; }); } else throw new Error('Subscription receive error: subscription event engine module disabled'); } // endregion // -------------------------------------------------------- // ------------------ Message Action API ------------------ // -------------------------------------------------------- // region Message Action API // region List /** * Get reactions to a specific message. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ public getMessageActions( parameters: MessageAction.GetMessageActionsParameters, callback: ResultCallback, ): void; /** * Get reactions to a specific message. * * @param parameters - Request configuration parameters. * * @returns Asynchronous get reactions response. */ public async getMessageActions( parameters: MessageAction.GetMessageActionsParameters, ): Promise; /** * Get reactions to a specific message. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous get reactions response or `void` in case if `callback` provided. */ async getMessageActions( parameters: MessageAction.GetMessageActionsParameters, callback?: ResultCallback, ): Promise { if (process.env.MESSAGE_REACTIONS_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Get message actions with parameters:', })); const request = new GetMessageActionsRequest({ ...parameters, keySet: this._configuration.keySet }); const logResponse = (response: MessageAction.GetMessageActionsResponse | null) => { if (!response) return; this.logger.debug('PubNub', `Get message actions success. Received ${response.data.length} message actions.`); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Get Message Actions error: message reactions module disabled'); } // endregion // region Add /** * Add a reaction to a specific message. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ public addMessageAction( parameters: MessageAction.AddMessageActionParameters, callback: ResultCallback, ): void; /** * Add a reaction to a specific message. * * @param parameters - Request configuration parameters. * * @returns Asynchronous add a reaction response. */ public async addMessageAction( parameters: MessageAction.AddMessageActionParameters, ): Promise; /** * Add a reaction to a specific message. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous add a reaction response or `void` in case if `callback` provided. */ async addMessageAction( parameters: MessageAction.AddMessageActionParameters, callback?: ResultCallback, ): Promise { if (process.env.MESSAGE_REACTIONS_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Add message action with parameters:', })); const request = new AddMessageActionRequest({ ...parameters, keySet: this._configuration.keySet }); const logResponse = (response: MessageAction.AddMessageActionResponse | null) => { if (!response) return; this.logger.debug( 'PubNub', `Message action add success. Message action added with timetoken: ${response.data.actionTimetoken}`, ); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Add Message Action error: message reactions module disabled'); } // endregion // region Remove /** * Remove a reaction from a specific message. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ public removeMessageAction( parameters: MessageAction.RemoveMessageActionParameters, callback: ResultCallback, ): void; /** * Remove a reaction from a specific message. * * @param parameters - Request configuration parameters. * * @returns Asynchronous remove a reaction response. */ public async removeMessageAction( parameters: MessageAction.RemoveMessageActionParameters, ): Promise; /** * Remove a reaction from a specific message. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous remove a reaction response or `void` in case if `callback` provided. */ async removeMessageAction( parameters: MessageAction.RemoveMessageActionParameters, callback?: ResultCallback, ): Promise { if (process.env.MESSAGE_REACTIONS_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Remove message action with parameters:', })); const request = new RemoveMessageAction({ ...parameters, keySet: this._configuration.keySet }); const logResponse = (response: MessageAction.RemoveMessageActionResponse | null) => { if (!response) return; this.logger.debug( 'PubNub', `Message action remove success. Removed message action with ${parameters.actionTimetoken} timetoken.`, ); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Remove Message Action error: message reactions module disabled'); } // endregion // endregion // -------------------------------------------------------- // --------------- Message Persistence API ---------------- // -------------------------------------------------------- // region Message Persistence API // region Fetch Messages /** * Fetch messages history for channels. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ public fetchMessages( parameters: History.FetchMessagesParameters, callback: ResultCallback, ): void; /** * Fetch messages history for channels. * * @param parameters - Request configuration parameters. * * @returns Asynchronous fetch messages response. */ public async fetchMessages(parameters: History.FetchMessagesParameters): Promise; /** * Fetch messages history for channels. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous fetch messages response or `void` in case if `callback` provided. */ async fetchMessages( parameters: History.FetchMessagesParameters, callback?: ResultCallback, ): Promise { if (process.env.MESSAGE_PERSISTENCE_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Fetch messages with parameters:', })); const request = new FetchMessagesRequest({ ...parameters, keySet: this._configuration.keySet, crypto: this._configuration.getCryptoModule(), getFileUrl: this.getFileUrl.bind(this), }); const logResponse = (response: History.FetchMessagesResponse | null) => { if (!response) return; const messagesCount = Object.values(response.channels).reduce((acc, message) => acc + message.length, 0); this.logger.debug('PubNub', `Fetch messages success. Received ${messagesCount} messages.`); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Fetch Messages History error: message persistence module disabled'); } // endregion // region Delete Messages /** * Delete messages from the channel history. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. * */ public deleteMessages( parameters: History.DeleteMessagesParameters, callback: ResultCallback, ): void; /** * Delete messages from the channel history. * * @param parameters - Request configuration parameters. * * @returns Asynchronous delete messages response. * */ public async deleteMessages(parameters: History.DeleteMessagesParameters): Promise; /** * Delete messages from the channel history. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous delete messages response or `void` in case if `callback` provided. * */ async deleteMessages( parameters: History.DeleteMessagesParameters, callback?: ResultCallback, ): Promise { if (process.env.MESSAGE_PERSISTENCE_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Delete messages with parameters:', })); const request = new DeleteMessageRequest({ ...parameters, keySet: this._configuration.keySet }); const logResponse = (response: History.DeleteMessagesResponse | null) => { if (!response) return; this.logger.debug('PubNub', `Delete messages success.`); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Delete Messages error: message persistence module disabled'); } // endregion // region Count Messages /** * Count messages from the channels' history. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ public messageCounts( parameters: History.MessageCountParameters, callback: ResultCallback, ): void; /** * Count messages from the channels' history. * * @param parameters - Request configuration parameters. * * @returns Asynchronous count messages response. */ public async messageCounts(parameters: History.MessageCountParameters): Promise; /** * Count messages from the channels' history. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous count messages response or `void` in case if `callback` provided. */ async messageCounts( parameters: History.MessageCountParameters, callback?: ResultCallback, ): Promise { if (process.env.MESSAGE_PERSISTENCE_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Get messages count with parameters:', })); const request = new MessageCountRequest({ ...parameters, keySet: this._configuration.keySet }); const logResponse = (response: History.MessageCountResponse | null) => { if (!response) return; const messagesCount = Object.values(response.channels).reduce((acc, messagesCount) => acc + messagesCount, 0); this.logger.debug( 'PubNub', `Get messages count success. There are ${messagesCount} messages since provided reference timetoken${ parameters.channelTimetokens ? parameters.channelTimetokens.join(',') : ''.length > 1 ? 's' : '' }.`, ); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Get Messages Count error: message persistence module disabled'); } // endregion // region Deprecated // region Fetch History /** * Fetch single channel history. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. * * @deprecated */ public history(parameters: History.GetHistoryParameters, callback: ResultCallback): void; /** * Fetch single channel history. * * @param parameters - Request configuration parameters. * * @returns Asynchronous fetch channel history response. * * @deprecated */ public async history(parameters: History.GetHistoryParameters): Promise; /** * Fetch single channel history. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous fetch channel history response or `void` in case if `callback` provided. * * @deprecated */ async history( parameters: History.GetHistoryParameters, callback?: ResultCallback, ): Promise { if (process.env.MESSAGE_PERSISTENCE_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Fetch history with parameters:', })); const request = new GetHistoryRequest({ ...parameters, keySet: this._configuration.keySet, crypto: this._configuration.getCryptoModule(), }); const logResponse = (response: History.GetHistoryResponse | null) => { if (!response) return; this.logger.debug('PubNub', `Fetch history success. Received ${response.messages.length} messages.`); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Get Messages History error: message persistence module disabled'); } // endregion // endregion // endregion // -------------------------------------------------------- // --------------------- Presence API --------------------- // -------------------------------------------------------- // region Presence API // region Here Now /** * Get channel's presence information. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ public hereNow(parameters: Presence.HereNowParameters, callback: ResultCallback): void; /** * Get channel presence information. * * @param parameters - Request configuration parameters. * * @returns Asynchronous get channel's presence response. */ public async hereNow(parameters: Presence.HereNowParameters): Promise; /** * Get channel's presence information. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous get channel's presence response or `void` in case if `callback` provided. */ async hereNow( parameters: Presence.HereNowParameters, callback?: ResultCallback, ): Promise { if (process.env.PRESENCE_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Here now with parameters:', })); const request = new HereNowRequest({ ...parameters, keySet: this._configuration.keySet }); const logResponse = (response: Presence.HereNowResponse | null) => { if (!response) return; this.logger.debug( 'PubNub', `Here now success. There are ${response.totalOccupancy} participants in ${response.totalChannels} channels.`, ); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Get Channel Here Now error: presence module disabled'); } // endregion // region Where Now /** * Get user's presence information. * * Get list of channels to which `uuid` currently subscribed. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ public whereNow(parameters: Presence.WhereNowParameters, callback: ResultCallback): void; /** * Get user's presence information. * * Get list of channels to which `uuid` currently subscribed. * * @param parameters - Request configuration parameters. * * @returns Asynchronous get user's presence response. */ public async whereNow(parameters: Presence.WhereNowParameters): Promise; /** * Get user's presence information. * * Get list of channels to which `uuid` currently subscribed. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous get user's presence response or `void` in case if `callback` provided. */ async whereNow( parameters: Presence.WhereNowParameters, callback?: ResultCallback, ): Promise { if (process.env.PRESENCE_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Where now with parameters:', })); const request = new WhereNowRequest({ uuid: parameters.uuid ?? this._configuration.userId!, keySet: this._configuration.keySet, }); const logResponse = (response: Presence.WhereNowResponse | null) => { if (!response) return; this.logger.debug('PubNub', `Where now success. Currently present in ${response.channels.length} channels.`); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Get UUID Here Now error: presence module disabled'); } // endregion // region Get Presence State /** * Get associated user's data for channels and groups. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ public getState( parameters: Presence.GetPresenceStateParameters, callback: ResultCallback, ): void; /** * Get associated user's data for channels and groups. * * @param parameters - Request configuration parameters. * * @returns Asynchronous get associated user's data response. */ public async getState(parameters: Presence.GetPresenceStateParameters): Promise; /** * Get associated user's data for channels and groups. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous get user's data response or `void` in case if `callback` provided. */ async getState( parameters: Presence.GetPresenceStateParameters, callback?: ResultCallback, ): Promise { if (process.env.PRESENCE_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Get presence state with parameters:', })); const request = new GetPresenceStateRequest({ ...parameters, uuid: parameters.uuid ?? this._configuration.userId!, keySet: this._configuration.keySet, }); const logResponse = (response: Presence.GetPresenceStateResponse | null) => { if (!response) return; this.logger.debug( 'PubNub', `Get presence state success. Received presence state for ${Object.keys(response.channels).length} channels.`, ); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Get UUID State error: presence module disabled'); } // endregion // region Set Presence State /** * Set associated user's data for channels and groups. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ public setState( parameters: Presence.SetPresenceStateParameters | Presence.SetPresenceStateWithHeartbeatParameters, callback: ResultCallback, ): void; /** * Set associated user's data for channels and groups. * * @param parameters - Request configuration parameters. * * @returns Asynchronous set associated user's data response. */ public async setState( parameters: Presence.SetPresenceStateParameters | Presence.SetPresenceStateWithHeartbeatParameters, ): Promise; /** * Set associated user's data for channels and groups. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous set user's data response or `void` in case if `callback` provided. */ async setState( parameters: Presence.SetPresenceStateParameters | Presence.SetPresenceStateWithHeartbeatParameters, callback?: ResultCallback, ): Promise { if (process.env.PRESENCE_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Set presence state with parameters:', })); const { keySet, userId: userId } = this._configuration; const heartbeat = this._configuration.getPresenceTimeout(); let request: AbstractRequest< Presence.PresenceHeartbeatResponse | Presence.SetPresenceStateResponse, Record >; // Maintain presence information (if required). if (this._configuration.enableEventEngine && this.presenceState) { const presenceState = this.presenceState; parameters.channels?.forEach((channel) => (presenceState[channel] = parameters.state)); if ('channelGroups' in parameters) { parameters.channelGroups?.forEach((group) => (presenceState[group] = parameters.state)); } if (this.onPresenceStateChange) this.onPresenceStateChange(this.presenceState); } // Check whether the state should be set with heartbeat or not. if ('withHeartbeat' in parameters && parameters.withHeartbeat) { request = new HeartbeatRequest({ ...parameters, keySet, heartbeat }); } else { request = new SetPresenceStateRequest({ ...parameters, keySet, uuid: userId! }); } const logResponse = (response: Presence.SetPresenceStateResponse | Presence.PresenceHeartbeatResponse | null) => { if (!response) return; this.logger.debug( 'PubNub', `Set presence state success.${ request instanceof HeartbeatRequest ? ' Presence state has been set using heartbeat endpoint.' : '' }`, ); }; // Update state used by subscription manager. if (this.subscriptionManager) { this.subscriptionManager.setState(parameters); if (this.onPresenceStateChange) this.onPresenceStateChange(this.subscriptionManager.presenceState); } if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Set UUID State error: presence module disabled'); } // endregion // region Change presence state /** * Manual presence management. * * @param parameters - Desired presence state for a provided list of channels and groups. */ public presence(parameters: { connected: boolean; channels?: string[]; channelGroups?: string[] }) { if (process.env.SUBSCRIBE_MANAGER_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Change presence with parameters:', })); this.subscriptionManager?.changePresence(parameters); } else throw new Error('Change UUID presence error: subscription manager module disabled'); } // endregion // region Heartbeat /** * Announce user presence * * @internal * * @param parameters - Desired presence state for provided list of channels and groups. * @param callback - Request completion handler callback. */ private async heartbeat( parameters: Presence.CancelablePresenceHeartbeatParameters, callback?: ResultCallback, ) { if (process.env.PRESENCE_MODULE !== 'disabled') { this.logger.trace('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Heartbeat with parameters:', })); // Filtering out presence channels and groups. let { channels, channelGroups } = parameters; // Remove `-pnpres` channels / groups if they not acceptable in the current PubNub client configuration. if (channelGroups) channelGroups = channelGroups.filter((channelGroup) => !channelGroup.endsWith('-pnpres')); if (channels) channels = channels.filter((channel) => !channel.endsWith('-pnpres')); // Complete immediately request only for presence channels. if ((channelGroups ?? []).length === 0 && (channels ?? []).length === 0) { const responseStatus = { error: false, operation: RequestOperation.PNHeartbeatOperation, category: StatusCategory.PNAcknowledgmentCategory, statusCode: 200, }; this.logger.trace('PubNub', 'There are no active subscriptions. Ignore.'); if (callback) return callback(responseStatus, {}); return Promise.resolve(responseStatus); } const request = new HeartbeatRequest({ ...parameters, channels: [...new Set(channels)], channelGroups: [...new Set(channelGroups)], keySet: this._configuration.keySet, }); const logResponse = (response: Presence.PresenceHeartbeatResponse | null) => { if (!response) return; this.logger.trace('PubNub', 'Heartbeat success.'); }; const abortUnsubscribe = parameters.abortSignal?.subscribe((err) => { request.abort('Cancel long-poll subscribe request'); }); if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); if (abortUnsubscribe) abortUnsubscribe(); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); if (abortUnsubscribe) abortUnsubscribe(); return response; }); } else throw new Error('Announce UUID Presence error: presence module disabled'); } // endregion // region Join /** * Announce user `join` on specified list of channels and groups. * * @internal * * @param parameters - List of channels and groups where `join` event should be sent. */ private join(parameters: { channels?: string[]; groups?: string[] }) { if (process.env.PRESENCE_MODULE !== 'disabled') { this.logger.trace('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Join with parameters:', })); if (parameters && (parameters.channels ?? []).length === 0 && (parameters.groups ?? []).length === 0) { this.logger.trace('PubNub', "Ignoring 'join' announcement request."); return; } if (this.presenceEventEngine) this.presenceEventEngine.join(parameters); else { this.heartbeat( { channels: parameters.channels, channelGroups: parameters.groups, ...(this._configuration.maintainPresenceState && this.presenceState && Object.keys(this.presenceState).length > 0 && { state: this.presenceState }), heartbeat: this._configuration.getPresenceTimeout(), }, () => {}, ); } } else throw new Error('Announce UUID Presence error: presence module disabled'); } /** * Reconnect presence event engine after network issues. * * @param parameters - List of channels and groups where `join` event should be sent. * * @internal */ private presenceReconnect(parameters: { channels?: string[]; groups?: string[] }) { if (process.env.PRESENCE_MODULE !== 'disabled') { this.logger.trace('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Presence reconnect with parameters:', })); if (this.presenceEventEngine) this.presenceEventEngine.reconnect(); else { this.heartbeat( { channels: parameters.channels, channelGroups: parameters.groups, ...(this._configuration.maintainPresenceState && { state: this.presenceState }), heartbeat: this._configuration.getPresenceTimeout(), }, () => {}, ); } } else throw new Error('Announce UUID Presence error: presence module disabled'); } // endregion // region Leave /** * Announce user `leave` on specified list of channels and groups. * * @internal * * @param parameters - List of channels and groups where `leave` event should be sent. */ private leave(parameters: { channels?: string[]; groups?: string[] }) { if (process.env.PRESENCE_MODULE !== 'disabled') { this.logger.trace('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Leave with parameters:', })); if (parameters && (parameters.channels ?? []).length === 0 && (parameters.groups ?? []).length === 0) { this.logger.trace('PubNub', "Ignoring 'leave' announcement request."); return; } if (this.presenceEventEngine) this.presenceEventEngine?.leave(parameters); else this.makeUnsubscribe({ channels: parameters.channels, channelGroups: parameters.groups }, () => {}); } else throw new Error('Announce UUID Leave error: presence module disabled'); } /** * Announce user `leave` on all subscribed channels. * * @internal * * @param parameters - List of channels and groups where `leave` event should be sent. */ private leaveAll(parameters: { channels?: string[]; groups?: string[]; isOffline?: boolean } = {}) { if (process.env.PRESENCE_MODULE !== 'disabled') { this.logger.trace('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Leave all with parameters:', })); if (this.presenceEventEngine) this.presenceEventEngine.leaveAll(!!parameters.isOffline); else if (!parameters.isOffline) this.makeUnsubscribe({ channels: parameters.channels, channelGroups: parameters.groups }, () => {}); } else throw new Error('Announce UUID Leave error: presence module disabled'); } /** * Announce user `leave` on disconnection. * * @internal * * @param parameters - List of channels and groups where `leave` event should be sent. */ private presenceDisconnect(parameters: { channels?: string[]; groups?: string[]; isOffline?: boolean }) { if (process.env.PRESENCE_MODULE !== 'disabled') { this.logger.trace('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Presence disconnect parameters:', })); if (this.presenceEventEngine) this.presenceEventEngine.disconnect(!!parameters.isOffline); else if (!parameters.isOffline) this.makeUnsubscribe({ channels: parameters.channels, channelGroups: parameters.groups }, () => {}); } else throw new Error('Announce UUID Leave error: presence module disabled'); } // endregion // endregion // -------------------------------------------------------- // ------------------------ PAM API ----------------------- // -------------------------------------------------------- // region PAM API // region Grant /** * Grant token permission. * * Generate access token with requested permissions. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ public grantToken(parameters: PAM.GrantTokenParameters, callback: ResultCallback): void; /** * Grant token permission. * * Generate an access token with requested permissions. * * @param parameters - Request configuration parameters. * * @returns Asynchronous grant token response. */ public async grantToken(parameters: PAM.GrantTokenParameters): Promise; /** * Grant token permission. * * Generate an access token with requested permissions. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous grant token response or `void` in case if `callback` provided. */ async grantToken( parameters: PAM.GrantTokenParameters, callback?: ResultCallback, ): Promise { if (process.env.PAM_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Grant token permissions with parameters:', })); const request = new GrantTokenRequest({ ...parameters, keySet: this._configuration.keySet }); const logResponse = (response: PAM.GrantTokenResponse | null) => { if (!response) return; this.logger.debug( 'PubNub', `Grant token permissions success. Received token with requested permissions: ${response}`, ); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Grant Token error: PAM module disabled'); } // endregion // region Revoke /** * Revoke token permission. * * @param token - Access token for which permissions should be revoked. * @param callback - Request completion handler callback. */ public revokeToken(token: PAM.RevokeParameters, callback: ResultCallback): void; /** * Revoke token permission. * * @param token - Access token for which permissions should be revoked. * * @returns Asynchronous revoke token response. */ public async revokeToken(token: PAM.RevokeParameters): Promise; /** * Revoke token permission. * * @param token - Access token for which permissions should be revoked. * @param [callback] - Request completion handler callback. * * @returns Asynchronous revoke token response or `void` in case if `callback` provided. */ async revokeToken( token: PAM.RevokeParameters, callback?: ResultCallback, ): Promise { if (process.env.PAM_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { token }, details: 'Revoke token permissions with parameters:', })); const request = new RevokeTokenRequest({ token, keySet: this._configuration.keySet }); const logResponse = (response: PAM.RevokeTokenResponse | null) => { if (!response) return; this.logger.debug('PubNub', 'Revoke token permissions success.'); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Revoke Token error: PAM module disabled'); } // endregion // region Token Manipulation /** * Get a current access token. * * @returns Previously configured access token using {@link setToken} method. */ public get token(): string | undefined { return this.tokenManager && this.tokenManager.getToken(); } /** * Get a current access token. * * @returns Previously configured access token using {@link setToken} method. */ public getToken(): string | undefined { return this.token; } /** * Set current access token. * * @param token - New access token which should be used with next REST API endpoint calls. */ public set token(token: string | undefined) { if (this.tokenManager) this.tokenManager.setToken(token); if (this.onAuthenticationChange) this.onAuthenticationChange(token); } /** * Set current access token. * * @param token - New access token which should be used with next REST API endpoint calls. */ public setToken(token: string | undefined): void { this.token = token; } /** * Parse access token. * * Parse token to see what permissions token owner has. * * @param token - Token which should be parsed. * * @returns Token's permissions information for the resources. */ public parseToken(token: string): PAM.Token | undefined { return this.tokenManager && this.tokenManager.parseToken(token); } // endregion // region Deprecated // region Grant Auth Permissions /** * Grant auth key(s) permission. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. * * @deprecated Use {@link grantToken} and {@link setToken} methods instead. */ public grant(parameters: PAM.GrantParameters, callback: ResultCallback): void; /** * Grant auth key(s) permission. * * @param parameters - Request configuration parameters. * * @returns Asynchronous grant auth key(s) permissions response. * * @deprecated Use {@link grantToken} and {@link setToken} methods instead. */ public async grant(parameters: PAM.GrantParameters): Promise; /** * Grant auth key(s) permission. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous grant auth key(s) permissions or `void` in case if `callback` provided. * * @deprecated Use {@link grantToken} and {@link setToken} methods instead. */ async grant( parameters: PAM.GrantParameters, callback?: ResultCallback, ): Promise { if (process.env.PAM_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Grant auth key(s) permissions with parameters:', })); const request = new GrantRequest({ ...parameters, keySet: this._configuration.keySet }); const logResponse = (response: PAM.PermissionsResponse | null) => { if (!response) return; this.logger.debug('PubNub', 'Grant auth key(s) permissions success.'); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Grant error: PAM module disabled'); } // endregion // region Audit Permissions /** * Audit auth key(s) permission. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. * * @deprecated */ public audit(parameters: PAM.AuditParameters, callback: ResultCallback): void; /** * Audit auth key(s) permission. * * @param parameters - Request configuration parameters. * * @returns Asynchronous audit auth key(s) permissions response. * * @deprecated */ public async audit(parameters: PAM.AuditParameters): Promise; /** * Audit auth key(s) permission. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @deprecated * * @deprecated Use {@link grantToken} and {@link setToken} methods instead. */ async audit( parameters: PAM.AuditParameters, callback?: ResultCallback, ): Promise { if (process.env.PAM_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: 'Audit auth key(s) permissions with parameters:', })); const request = new AuditRequest({ ...parameters, keySet: this._configuration.keySet }); const logResponse = (response: PAM.PermissionsResponse | null) => { if (!response) return; this.logger.debug('PubNub', 'Audit auth key(s) permissions success.'); }; if (callback) { return this.sendRequest(request, (status, response) => { logResponse(response); return callback(status, response); }); } return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Grant Permissions error: PAM module disabled'); } // endregion // endregion // endregion // -------------------------------------------------------- // ------------------- App Context API -------------------- // -------------------------------------------------------- // region App Context API /** * PubNub App Context API group. */ get objects(): PubNubObjects { return this._objects; } // region Deprecated API /** * Fetch a paginated list of User objects. * * @param callback - Request completion handler callback. * * @deprecated Use {@link PubNubCore#objects.getAllUUIDMetadata getAllUUIDMetadata} method instead. */ public fetchUsers( callback: ResultCallback>, ): void; /** * Fetch a paginated list of User objects. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. * * @deprecated Use {@link PubNubCore#objects.getAllUUIDMetadata getAllUUIDMetadata} method instead. */ public fetchUsers( parameters: AppContext.GetAllMetadataParameters>, callback: ResultCallback>, ): void; /** * Fetch a paginated list of User objects. * * @param [parameters] - Request configuration parameters. * * @returns Asynchronous get all User objects response. * * @deprecated Use {@link PubNubCore#objects.getAllUUIDMetadata getAllUUIDMetadata} method instead. */ public async fetchUsers( parameters?: AppContext.GetAllMetadataParameters>, ): Promise>; /** Fetch a paginated list of User objects. * * @param [parametersOrCallback] - Request configuration parameters or callback from overload. * @param [callback] - Request completion handler callback. * * @returns Asynchronous get all User objects response or `void` in case if `callback` provided. * * @deprecated Use {@link PubNubCore#objects.getAllUUIDMetadata getAllUUIDMetadata} method instead. */ public async fetchUsers( parametersOrCallback?: | AppContext.GetAllMetadataParameters> | ResultCallback>, callback?: ResultCallback>, ): Promise | void> { if (process.env.APP_CONTEXT_MODULE !== 'disabled') { this.logger.warn('PubNub', "'fetchUsers' is deprecated. Use 'pubnub.objects.getAllUUIDMetadata' instead."); this.logger.debug('PubNub', () => ({ messageType: 'object', message: !parametersOrCallback || typeof parametersOrCallback === 'function' ? {} : parametersOrCallback, details: `Fetch all User objects with parameters:`, })); return this.objects._getAllUUIDMetadata(parametersOrCallback, callback); } else throw new Error('Fetch Users Metadata error: App Context module disabled'); } /** * Fetch User object for a currently configured PubNub client `uuid`. * * @param callback - Request completion handler callback. * * @deprecated Use {@link PubNubCore#objects.getUUIDMetadata getUUIDMetadata} method instead. */ public fetchUser( callback: ResultCallback>, ): void; /** * Fetch User object for a currently configured PubNub client `uuid`. * * @param parameters - Request configuration parameters. Will fetch a User object for a currently * configured PubNub client `uuid` if not set. * @param callback - Request completion handler callback. * * @deprecated Use {@link PubNubCore#objects.getUUIDMetadata|getUUIDMetadata} method instead. */ public fetchUser( parameters: AppContext.GetUUIDMetadataParameters, callback: ResultCallback>, ): void; /** * Fetch User object for a currently configured PubNub client `uuid`. * * @param [parameters] - Request configuration parameters. Will fetch a User object for a currently * configured PubNub client `uuid` if not set. * * @returns Asynchronous get User object response. * * @deprecated Use {@link PubNubCore#objects.getUUIDMetadata getUUIDMetadata} method instead. */ public async fetchUser( parameters?: AppContext.GetUUIDMetadataParameters, ): Promise>; /** * Fetch User object for a currently configured PubNub client `uuid`. * * @param [parametersOrCallback] - Request configuration parameters or callback from overload. * @param [callback] - Request completion handler callback. * * @returns Asynchronous get User object response or `void` in case if `callback` provided. * * @deprecated Use {@link PubNubCore#objects.getUUIDMetadata getUUIDMetadata} method instead. */ async fetchUser( parametersOrCallback?: | AppContext.GetUUIDMetadataParameters | ResultCallback>, callback?: ResultCallback>, ): Promise | void> { if (process.env.APP_CONTEXT_MODULE !== 'disabled') { this.logger.warn('PubNub', "'fetchUser' is deprecated. Use 'pubnub.objects.getUUIDMetadata' instead."); this.logger.debug('PubNub', () => ({ messageType: 'object', message: !parametersOrCallback || typeof parametersOrCallback === 'function' ? { uuid: this.userId } : parametersOrCallback, details: `Fetch${ !parametersOrCallback || typeof parametersOrCallback === 'function' ? ' current' : '' } User object with parameters:`, })); return this.objects._getUUIDMetadata(parametersOrCallback, callback); } else throw new Error('Fetch User Metadata error: App Context module disabled'); } /** * Create a User object. * * @param parameters - Request configuration parameters. Will create a User object for a currently * configured PubNub client `uuid` if not set. * @param callback - Request completion handler callback. * * @deprecated Use {@link PubNubCore#objects.setUUIDMetadata setUUIDMetadata} method instead. */ public createUser( parameters: AppContext.SetUUIDMetadataParameters, callback: ResultCallback>, ): void; /** * Create a User object. * * @param parameters - Request configuration parameters. Will create User object for a currently * configured PubNub client `uuid` if not set. * * @returns Asynchronous create User object response. * * @deprecated Use {@link PubNubCore#objects.setUUIDMetadata setUUIDMetadata} method instead. */ public async createUser( parameters: AppContext.SetUUIDMetadataParameters, ): Promise>; /** * Create a User object. * * @param parameters - Request configuration parameters. Will create a User object for a currently * configured PubNub client `uuid` if not set. * @param [callback] - Request completion handler callback. * * @returns Asynchronous create User object response or `void` in case if `callback` provided. * * @deprecated Use {@link PubNubCore#objects.setUUIDMetadata setUUIDMetadata} method instead. */ async createUser( parameters: AppContext.SetUUIDMetadataParameters, callback?: ResultCallback>, ): Promise | void> { if (process.env.APP_CONTEXT_MODULE !== 'disabled') { this.logger.warn('PubNub', "'createUser' is deprecated. Use 'pubnub.objects.setUUIDMetadata' instead."); this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: `Create User object with parameters:`, })); return this.objects._setUUIDMetadata(parameters, callback); } else throw new Error('Create User Metadata error: App Context module disabled'); } /** * Update a User object. * * @param parameters - Request configuration parameters. Will update User object for currently * configured PubNub client `uuid` if not set. * @param callback - Request completion handler callback. * * @deprecated Use {@link PubNubCore#objects.setUUIDMetadata setUUIDMetadata} method instead. */ public updateUser( parameters: AppContext.SetUUIDMetadataParameters, callback: ResultCallback>, ): void; /** * Update a User object. * * @param parameters - Request configuration parameters. Will update a User object for a currently * configured PubNub client `uuid` if not set. * * @returns Asynchronous update User object response. * * @deprecated Use {@link PubNubCore#objects.setUUIDMetadata setUUIDMetadata} method instead. */ public async updateUser( parameters: AppContext.SetUUIDMetadataParameters, ): Promise>; /** * Update a User object. * * @param parameters - Request configuration parameters. Will update a User object for a currently * configured PubNub client `uuid` if not set. * @param [callback] - Request completion handler callback. * * @returns Asynchronous update User object response or `void` in case if `callback` provided. * * @deprecated Use {@link PubNubCore#objects.setUUIDMetadata setUUIDMetadata} method instead. */ async updateUser( parameters: AppContext.SetUUIDMetadataParameters, callback?: ResultCallback>, ): Promise | void> { this.logger.warn('PubNub', "'updateUser' is deprecated. Use 'pubnub.objects.setUUIDMetadata' instead."); if (process.env.APP_CONTEXT_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: `Update User object with parameters:`, })); return this.objects._setUUIDMetadata(parameters, callback); } else throw new Error('Update User Metadata error: App Context module disabled'); } /** * Remove a specific User object. * * @param callback - Request completion handler callback. Will remove a User object for a currently * configured PubNub client `uuid` if not set. * * @deprecated Use {@link PubNubCore#objects.removeUUIDMetadata removeUUIDMetadata} method instead. */ public removeUser(callback: ResultCallback): void; /** * Remove a specific User object. * * @param parameters - Request configuration parameters. Will remove a User object for a currently * configured PubNub client `uuid` if not set. * @param callback - Request completion handler callback. * * @deprecated Use {@link PubNubCore#objects.removeUUIDMetadata removeUUIDMetadata} method instead. */ public removeUser( parameters: AppContext.RemoveUUIDMetadataParameters, callback: ResultCallback, ): void; /** * Remove a specific User object. * * @param [parameters] - Request configuration parameters. Will remove a User object for a currently * configured PubNub client `uuid` if not set. * * @returns Asynchronous User object remove response. * * @deprecated Use {@link PubNubCore#objects.removeUUIDMetadata removeUUIDMetadata} method instead. */ public async removeUser( parameters?: AppContext.RemoveUUIDMetadataParameters, ): Promise; /** * Remove a specific User object. * * @param [parametersOrCallback] - Request configuration parameters or callback from overload. * @param [callback] - Request completion handler callback. * * @returns Asynchronous User object removes response or `void` in case if `callback` provided. * * @deprecated Use {@link PubNubCore#objects.removeUUIDMetadata removeUUIDMetadata} method instead. */ public async removeUser( parametersOrCallback?: | AppContext.RemoveUUIDMetadataParameters | ResultCallback, callback?: ResultCallback, ): Promise { if (process.env.APP_CONTEXT_MODULE !== 'disabled') { this.logger.warn('PubNub', "'removeUser' is deprecated. Use 'pubnub.objects.removeUUIDMetadata' instead."); this.logger.debug('PubNub', () => ({ messageType: 'object', message: !parametersOrCallback || typeof parametersOrCallback === 'function' ? { uuid: this.userId } : parametersOrCallback, details: `Remove${ !parametersOrCallback || typeof parametersOrCallback === 'function' ? ' current' : '' } User object with parameters:`, })); return this.objects._removeUUIDMetadata(parametersOrCallback, callback); } else throw new Error('Remove User Metadata error: App Context module disabled'); } /** * Fetch a paginated list of Space objects. * * @param callback - Request completion handler callback. * * @deprecated Use {@link PubNubCore#objects.getAllChannelMetadata getAllChannelMetadata} method instead. */ public fetchSpaces( callback: ResultCallback>, ): void; /** * Fetch a paginated list of Space objects. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. * * @deprecated Use {@link PubNubCore#objects.getAllChannelMetadata getAllChannelMetadata} method instead. */ public fetchSpaces( parameters: AppContext.GetAllMetadataParameters>, callback: ResultCallback>, ): void; /** * Fetch a paginated list of Space objects. * * @param [parameters] - Request configuration parameters. * * @returns Asynchronous get all Space objects responses. * * @deprecated Use {@link PubNubCore#objects.getAllChannelMetadata getAllChannelMetadata} method instead. */ public async fetchSpaces( parameters?: AppContext.GetAllMetadataParameters>, ): Promise>; /** * Fetch a paginated list of Space objects. * * @param [parametersOrCallback] - Request configuration parameters or callback from overload. * @param [callback] - Request completion handler callback. * * @returns Asynchronous get all Space objects response or `void` in case if `callback` * provided. * * @deprecated Use {@link PubNubCore#objects.getAllChannelMetadata getAllChannelMetadata} method instead. */ async fetchSpaces( parametersOrCallback?: | AppContext.GetAllMetadataParameters> | ResultCallback>, callback?: ResultCallback>, ): Promise | void> { if (process.env.APP_CONTEXT_MODULE !== 'disabled') { this.logger.warn('PubNub', "'fetchSpaces' is deprecated. Use 'pubnub.objects.getAllChannelMetadata' instead."); this.logger.debug('PubNub', () => ({ messageType: 'object', message: !parametersOrCallback || typeof parametersOrCallback === 'function' ? {} : parametersOrCallback, details: `Fetch all Space objects with parameters:`, })); return this.objects._getAllChannelMetadata(parametersOrCallback, callback); } else throw new Error('Fetch Spaces Metadata error: App Context module disabled'); } /** * Fetch a specific Space object. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. * * @deprecated Use {@link PubNubCore#objects.getChannelMetadata getChannelMetadata} method instead. */ public fetchSpace( parameters: AppContext.GetChannelMetadataParameters, callback: ResultCallback>, ): void; /** * Fetch a specific Space object. * * @param parameters - Request configuration parameters. * * @returns Asynchronous get Channel metadata response. * * @deprecated Use {@link PubNubCore#objects.getChannelMetadata getChannelMetadata} method instead. */ public async fetchSpace( parameters: AppContext.GetChannelMetadataParameters, ): Promise>; /** * Fetch a specific Space object. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous get Space object response or `void` in case if `callback` provided. * * @deprecated Use {@link PubNubCore#objects.getChannelMetadata getChannelMetadata} method instead. */ async fetchSpace( parameters: AppContext.GetChannelMetadataParameters, callback?: ResultCallback>, ): Promise | void> { if (process.env.APP_CONTEXT_MODULE !== 'disabled') { this.logger.warn('PubNub', "'fetchSpace' is deprecated. Use 'pubnub.objects.getChannelMetadata' instead."); this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: `Fetch Space object with parameters:`, })); return this.objects._getChannelMetadata(parameters, callback); } else throw new Error('Fetch Space Metadata error: App Context module disabled'); } /** * Create a specific Space object. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. * * @deprecated Use {@link PubNubCore#objects.setChannelMetadata setChannelMetadata} method instead. */ public createSpace( parameters: AppContext.SetChannelMetadataParameters, callback: ResultCallback>, ): void; /** * Create specific Space object. * * @param parameters - Request configuration parameters. * * @returns Asynchronous create Space object response. * * @deprecated Use {@link PubNubCore#objects.setChannelMetadata setChannelMetadata} method instead. */ public async createSpace( parameters: AppContext.SetChannelMetadataParameters, ): Promise>; /** * Create specific Space object. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous create Space object response or `void` in case if `callback` provided. * * @deprecated Use {@link PubNubCore#objects.setChannelMetadata setChannelMetadata} method instead. */ async createSpace( parameters: AppContext.SetChannelMetadataParameters, callback?: ResultCallback>, ): Promise | void> { if (process.env.APP_CONTEXT_MODULE !== 'disabled') { this.logger.warn('PubNub', "'createSpace' is deprecated. Use 'pubnub.objects.setChannelMetadata' instead."); this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: `Create Space object with parameters:`, })); return this.objects._setChannelMetadata(parameters, callback); } else throw new Error('Create Space Metadata error: App Context module disabled'); } /** * Update specific Space object. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. * * @deprecated Use {@link PubNubCore#objects.setChannelMetadata setChannelMetadata} method instead. */ public updateSpace( parameters: AppContext.SetChannelMetadataParameters, callback: ResultCallback>, ): void; /** * Update specific Space object. * * @param parameters - Request configuration parameters. * * @returns Asynchronous update Space object response. * * @deprecated Use {@link PubNubCore#objects.setChannelMetadata setChannelMetadata} method instead. */ public async updateSpace( parameters: AppContext.SetChannelMetadataParameters, ): Promise>; /** * Update specific Space object. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous update Space object response or `void` in case if `callback` provided. * * @deprecated Use {@link PubNubCore#objects.setChannelMetadata setChannelMetadata} method instead. */ async updateSpace( parameters: AppContext.SetChannelMetadataParameters, callback?: ResultCallback>, ): Promise | void> { if (process.env.APP_CONTEXT_MODULE !== 'disabled') { this.logger.warn('PubNub', "'updateSpace' is deprecated. Use 'pubnub.objects.setChannelMetadata' instead."); this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: `Update Space object with parameters:`, })); return this.objects._setChannelMetadata(parameters, callback); } else throw new Error('Update Space Metadata error: App Context module disabled'); } /** * Remove a Space object. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. * * @deprecated Use {@link PubNubCore#objects.removeChannelMetadata removeChannelMetadata} method instead. */ public removeSpace( parameters: AppContext.RemoveChannelMetadataParameters, callback: ResultCallback, ): void; /** * Remove a specific Space object. * * @param parameters - Request configuration parameters. * * @returns Asynchronous Space object remove response. * * @deprecated Use {@link PubNubCore#objects.removeChannelMetadata removeChannelMetadata} method instead. */ public async removeSpace( parameters: AppContext.RemoveChannelMetadataParameters, ): Promise; /** * Remove a specific Space object. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous Space object remove response or `void` in case if `callback` * provided. * * @deprecated Use {@link PubNubCore#objects.removeChannelMetadata removeChannelMetadata} method instead. */ async removeSpace( parameters: AppContext.RemoveChannelMetadataParameters, callback?: ResultCallback, ): Promise { if (process.env.APP_CONTEXT_MODULE !== 'disabled') { this.logger.warn('PubNub', "'removeSpace' is deprecated. Use 'pubnub.objects.removeChannelMetadata' instead."); this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: `Remove Space object with parameters:`, })); return this.objects._removeChannelMetadata(parameters, callback); } else throw new Error('Remove Space Metadata error: App Context module disabled'); } /** * Fetch a paginated list of specific Space members or specific User memberships. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. * * @deprecated Use {@link PubNubCore#objects.getChannelMembers getChannelMembers} or * {@link PubNubCore#objects.getMemberships getMemberships} methods instead. */ public fetchMemberships< RelationCustom extends AppContext.CustomData = AppContext.CustomData, MetadataCustom extends AppContext.CustomData = AppContext.CustomData, >( parameters: AppContext.GetMembershipsParameters | AppContext.GetMembersParameters, callback: ResultCallback< | AppContext.SpaceMembershipsResponse | AppContext.UserMembersResponse >, ): void; /** * Fetch paginated list of specific Space members or specific User memberships. * * @param parameters - Request configuration parameters. * * @returns Asynchronous get specific Space members or specific User memberships response. * * @deprecated Use {@link PubNubCore#objects.getChannelMembers getChannelMembers} or * {@link PubNubCore#objects.getMemberships getMemberships} methods instead. */ public async fetchMemberships< RelationCustom extends AppContext.CustomData = AppContext.CustomData, MetadataCustom extends AppContext.CustomData = AppContext.CustomData, >( parameters: AppContext.GetMembershipsParameters | AppContext.GetMembersParameters, ): Promise< | AppContext.SpaceMembershipsResponse | AppContext.UserMembersResponse >; /** * Fetch a paginated list of specific Space members or specific User memberships. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous get specific Space members or specific User memberships response or * `void` in case if `callback` provided. * * @deprecated Use {@link PubNubCore#objects.getChannelMembers getChannelMembers} or * {@link PubNubCore#objects.getMemberships getMemberships} methods instead. */ public async fetchMemberships< RelationCustom extends AppContext.CustomData = AppContext.CustomData, MetadataCustom extends AppContext.CustomData = AppContext.CustomData, >( parameters: AppContext.GetMembershipsParameters | AppContext.GetMembersParameters, callback?: ResultCallback< | AppContext.SpaceMembershipsResponse | AppContext.UserMembersResponse >, ): Promise< | AppContext.SpaceMembershipsResponse | AppContext.UserMembersResponse | void > { if (process.env.APP_CONTEXT_MODULE !== 'disabled') return this.objects.fetchMemberships(parameters, callback); else throw new Error('Fetch Memberships error: App Context module disabled'); } /** * Add members to specific Space or memberships specific User. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. * * @deprecated Use {@link PubNubCore#objects.setChannelMembers setChannelMembers} or * {@link PubNubCore#objects.setMemberships setMemberships} methods instead. */ public addMemberships< Custom extends AppContext.CustomData = AppContext.CustomData, MetadataCustom extends AppContext.CustomData = AppContext.CustomData, >( parameters: AppContext.SetMembershipsParameters | AppContext.SetChannelMembersParameters, callback: ResultCallback< AppContext.SetMembershipsResponse | AppContext.SetMembersResponse >, ): void; /** * Add members to specific Space or memberships specific User. * * @param parameters - Request configuration parameters. * * @returns Asynchronous add members to specific Space or memberships specific User response. * * @deprecated Use {@link PubNubCore#objects.setChannelMembers setChannelMembers} or * {@link PubNubCore#objects.setMemberships setMemberships} methods instead. */ public async addMemberships< Custom extends AppContext.CustomData = AppContext.CustomData, MetadataCustom extends AppContext.CustomData = AppContext.CustomData, >( parameters: AppContext.SetMembershipsParameters | AppContext.SetChannelMembersParameters, ): Promise< AppContext.SetMembershipsResponse | AppContext.SetMembersResponse >; /** * Add members to specific Space or memberships specific User. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous add members to specific Space or memberships specific User response or * `void` in case if `callback` provided. * * @deprecated Use {@link PubNubCore#objects.setChannelMembers setChannelMembers} or * {@link PubNubCore#objects.setMemberships setMemberships} methods instead. */ async addMemberships< Custom extends AppContext.CustomData = AppContext.CustomData, MetadataCustom extends AppContext.CustomData = AppContext.CustomData, >( parameters: AppContext.SetMembershipsParameters | AppContext.SetChannelMembersParameters, callback?: ResultCallback< AppContext.SetMembershipsResponse | AppContext.SetMembersResponse >, ) { if (process.env.APP_CONTEXT_MODULE !== 'disabled') return this.objects.addMemberships(parameters, callback); else throw new Error('Add Memberships error: App Context module disabled'); } /** * Update specific Space members or User memberships. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. * * @deprecated Use {@link PubNubCore#objects.setChannelMembers setChannelMembers} or * {@link PubNubCore#objects.setMemberships setMemberships} methods instead. */ public updateMemberships< Custom extends AppContext.CustomData = AppContext.CustomData, MetadataCustom extends AppContext.CustomData = AppContext.CustomData, >( parameters: AppContext.SetMembershipsParameters | AppContext.SetChannelMembersParameters, callback: ResultCallback< AppContext.SetMembershipsResponse | AppContext.SetMembersResponse >, ): void; /** * Update specific Space members or User memberships. * * @param parameters - Request configuration parameters. * * @returns Asynchronous update Space members or User memberships response. * * @deprecated Use {@link PubNubCore#objects.setChannelMembers setChannelMembers} or * {@link PubNubCore#objects.setMemberships setMemberships} methods instead. */ public async updateMemberships< Custom extends AppContext.CustomData = AppContext.CustomData, MetadataCustom extends AppContext.CustomData = AppContext.CustomData, >( parameters: AppContext.SetMembershipsParameters | AppContext.SetChannelMembersParameters, ): Promise< AppContext.SetMembershipsResponse | AppContext.SetMembersResponse >; /** * Update specific Space members or User memberships. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous update Space members or User memberships response or `void` in case * if `callback` provided. * * @deprecated Use {@link PubNubCore#objects.setChannelMembers setChannelMembers} or * {@link PubNubCore#objects.setMemberships setMemberships} methods instead. */ async updateMemberships< Custom extends AppContext.CustomData = AppContext.CustomData, MetadataCustom extends AppContext.CustomData = AppContext.CustomData, >( parameters: AppContext.SetMembershipsParameters | AppContext.SetChannelMembersParameters, callback?: ResultCallback< AppContext.SetMembershipsResponse | AppContext.SetMembersResponse >, ) { if (process.env.APP_CONTEXT_MODULE !== 'disabled') { this.logger.warn( 'PubNub', "'addMemberships' is deprecated. Use 'pubnub.objects.setChannelMembers' or 'pubnub.objects.setMemberships'" + ' instead.', ); this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: `Update memberships with parameters:`, })); return this.objects.addMemberships(parameters, callback); } else throw new Error('Update Memberships error: App Context module disabled'); } /** * Remove User membership. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. * * @deprecated Use {@link PubNubCore#objects.removeMemberships removeMemberships} or * {@link PubNubCore#objects.removeChannelMembers removeChannelMembers} methods instead from `objects` API group. */ public removeMemberships< RelationCustom extends AppContext.CustomData = AppContext.CustomData, MetadataCustom extends AppContext.CustomData = AppContext.CustomData, >( parameters: AppContext.RemoveMembersParameters | AppContext.RemoveMembershipsParameters, callback: ResultCallback< | AppContext.RemoveMembersResponse | AppContext.RemoveMembershipsResponse >, ): void; /** * Remove User membership. * * @param parameters - Request configuration parameters. * * @returns Asynchronous memberships modification response. * * @deprecated Use {@link PubNubCore#objects.removeMemberships removeMemberships} or * {@link PubNubCore#objects.removeChannelMembers removeChannelMembers} methods instead from `objects` API group. */ public async removeMemberships< RelationCustom extends AppContext.CustomData = AppContext.CustomData, MetadataCustom extends AppContext.CustomData = AppContext.CustomData, >( parameters: AppContext.RemoveMembersParameters | AppContext.RemoveMembershipsParameters, ): Promise>; /** * Remove User membership. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous memberships modification response or `void` in case if `callback` provided. * * @deprecated Use {@link PubNubCore#objects.removeMemberships removeMemberships} or * {@link PubNubCore#objects.removeChannelMembers removeChannelMembers} methods instead from `objects` API group. */ public async removeMemberships< RelationCustom extends AppContext.CustomData = AppContext.CustomData, MetadataCustom extends AppContext.CustomData = AppContext.CustomData, >( parameters: AppContext.RemoveMembersParameters | AppContext.RemoveMembershipsParameters, callback?: ResultCallback< | AppContext.RemoveMembersResponse | AppContext.RemoveMembershipsResponse >, ): Promise< | AppContext.RemoveMembersResponse | AppContext.RemoveMembershipsResponse | void > { if (process.env.APP_CONTEXT_MODULE !== 'disabled') { this.logger.warn( 'PubNub', "'removeMemberships' is deprecated. Use 'pubnub.objects.removeMemberships' or" + " 'pubnub.objects.removeChannelMembers' instead.", ); this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: `Remove memberships with parameters:`, })); if ('spaceId' in parameters) { const spaceParameters = parameters as AppContext.RemoveMembersParameters; const requestParameters = { channel: spaceParameters.spaceId ?? spaceParameters.channel, uuids: spaceParameters.userIds ?? spaceParameters.uuids, limit: 0, }; if (callback) return this.objects.removeChannelMembers(requestParameters, callback); return this.objects.removeChannelMembers(requestParameters); } const userParameters = parameters as AppContext.RemoveMembershipsParameters; const requestParameters = { uuid: userParameters.userId, channels: userParameters.spaceIds ?? userParameters.channels, limit: 0, }; if (callback) return this.objects.removeMemberships(requestParameters, callback); return this.objects.removeMemberships(requestParameters); } else throw new Error('Remove Memberships error: App Context module disabled'); } // endregion // endregion // -------------------------------------------------------- // ----------------- Channel Groups API ------------------- // -------------------------------------------------------- // region Channel Groups API /** * PubNub Channel Groups API group. */ get channelGroups(): PubNubChannelGroups { return this._channelGroups; } // endregion // -------------------------------------------------------- // ---------------- Push Notifications API ----------------- // -------------------------------------------------------- // region Push Notifications API /** * PubNub Push Notifications API group. */ get push(): PubNubPushNotifications { return this._push; } // endregion // -------------------------------------------------------- // ------------------ File sharing API -------------------- // -------------------------------------------------------- // region File sharing API // region Send /** * Share file to a specific channel. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ public sendFile( parameters: FileSharing.SendFileParameters, callback: ResultCallback, ): void; /** * Share file to a specific channel. * * @param parameters - Request configuration parameters. * * @returns Asynchronous file sharing response. */ public async sendFile( parameters: FileSharing.SendFileParameters, ): Promise; /** * Share file to a specific channel. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous file sharing response or `void` in case if `callback` provided. */ public async sendFile( parameters: FileSharing.SendFileParameters, callback?: ResultCallback, ): Promise { if (process.env.FILE_SHARING_MODULE !== 'disabled') { if (!this._configuration.PubNubFile) throw new Error("Validation failed: 'PubNubFile' not configured or file upload not supported by the platform."); this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: `Send file with parameters:`, })); const sendFileRequest = new SendFileRequest({ ...parameters, keySet: this._configuration.keySet, PubNubFile: this._configuration.PubNubFile, fileUploadPublishRetryLimit: this._configuration.fileUploadPublishRetryLimit, file: parameters.file, sendRequest: this.sendRequest.bind(this), publishFile: this.publishFile.bind(this), crypto: this._configuration.getCryptoModule(), cryptography: this.cryptography ? (this.cryptography as Cryptography) : undefined, }); const status: Status = { error: false, operation: RequestOperation.PNPublishFileOperation, category: StatusCategory.PNAcknowledgmentCategory, statusCode: 0, }; const logResponse = (response: FileSharing.SendFileResponse | null) => { if (!response) return; this.logger.debug('PubNub', `Send file success. File shared with ${response.id} ID.`); }; return sendFileRequest .process() .then((response) => { status.statusCode = response.status; logResponse(response); if (callback) return callback(status, response); return response; }) .catch((error: unknown) => { let errorStatus: Status | undefined; if (error instanceof PubNubError) errorStatus = error.status; else if (error instanceof PubNubAPIError) errorStatus = error.toStatus(status.operation!); this.logger.error('PubNub', () => ({ messageType: 'error', message: new PubNubError('File sending error. Check status for details', errorStatus), })); // Notify callback (if possible). if (callback && errorStatus) callback(errorStatus, null); throw new PubNubError('REST API request processing error. Check status for details', errorStatus); }); } else throw new Error('Send File error: file sharing module disabled'); } // endregion // region Publish File Message /** * Publish file message to a specific channel. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ public publishFile( parameters: FileSharing.PublishFileMessageParameters, callback: ResultCallback, ): void; /** * Publish file message to a specific channel. * * @param parameters - Request configuration parameters. * * @returns Asynchronous publish file message response. */ public async publishFile( parameters: FileSharing.PublishFileMessageParameters, ): Promise; /** * Publish file message to a specific channel. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous publish file message response or `void` in case if `callback` provided. */ public async publishFile( parameters: FileSharing.PublishFileMessageParameters, callback?: ResultCallback, ): Promise { if (process.env.FILE_SHARING_MODULE !== 'disabled') { if (!this._configuration.PubNubFile) throw new Error("Validation failed: 'PubNubFile' not configured or file upload not supported by the platform."); this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: `Publish file message with parameters:`, })); const request = new PublishFileMessageRequest({ ...parameters, keySet: this._configuration.keySet, crypto: this._configuration.getCryptoModule(), }); const logResponse = (response: FileSharing.PublishFileMessageResponse | null) => { if (!response) return; this.logger.debug( 'PubNub', `Publish file message success. File message published with timetoken: ${response.timetoken}`, ); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Publish File error: file sharing module disabled'); } // endregion // region List /** * Retrieve list of shared files in specific channel. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ public listFiles( parameters: FileSharing.ListFilesParameters, callback: ResultCallback, ): void; /** * Retrieve list of shared files in specific channel. * * @param parameters - Request configuration parameters. * * @returns Asynchronous shared files list response. */ public async listFiles(parameters: FileSharing.ListFilesParameters): Promise; /** * Retrieve list of shared files in specific channel. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous shared files list response or `void` in case if `callback` provided. */ public async listFiles( parameters: FileSharing.ListFilesParameters, callback?: ResultCallback, ): Promise { if (process.env.FILE_SHARING_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: `List files with parameters:`, })); const request = new FilesListRequest({ ...parameters, keySet: this._configuration.keySet }); const logResponse = (response: FileSharing.ListFilesResponse | null) => { if (!response) return; this.logger.debug('PubNub', `List files success. There are ${response.count} uploaded files.`); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('List Files error: file sharing module disabled'); } // endregion // region Get Download Url /** * Get file download Url. * * @param parameters - Request configuration parameters. * * @returns File download Url. */ public getFileUrl(parameters: FileSharing.FileUrlParameters): FileSharing.FileUrlResponse { if (process.env.FILE_SHARING_MODULE !== 'disabled') { const request = this.transport.request( new GetFileDownloadUrlRequest({ ...parameters, keySet: this._configuration.keySet }).request(), ); const query = request.queryParameters ?? {}; const queryString = Object.keys(query) .map((key) => { const queryValue = query[key]; if (!Array.isArray(queryValue)) return `${key}=${encodeString(queryValue)}`; return queryValue.map((value) => `${key}=${encodeString(value)}`).join('&'); }) .join('&'); return `${request.origin}${request.path}?${queryString}`; } else throw new Error('Generate File Download Url error: file sharing module disabled'); } // endregion // region Download /** * Download a shared file from a specific channel. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ public downloadFile(parameters: FileSharing.DownloadFileParameters, callback: ResultCallback): void; /** * Download a shared file from a specific channel. * * @param parameters - Request configuration parameters. * * @returns Asynchronous download shared file response. */ public async downloadFile(parameters: FileSharing.DownloadFileParameters): Promise; /** * Download a shared file from a specific channel. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous download shared file response or `void` in case if `callback` provided. */ public async downloadFile( parameters: FileSharing.DownloadFileParameters, callback?: ResultCallback, ): Promise { if (process.env.FILE_SHARING_MODULE !== 'disabled') { if (!this._configuration.PubNubFile) throw new Error("Validation failed: 'PubNubFile' not configured or file upload not supported by the platform."); this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: `Download file with parameters:`, })); const request = new DownloadFileRequest({ ...parameters, keySet: this._configuration.keySet, PubNubFile: this._configuration.PubNubFile, cryptography: this.cryptography ? (this.cryptography as Cryptography) : undefined, crypto: this._configuration.getCryptoModule(), }); const logResponse = (response: PlatformFile | null) => { if (!response) return; this.logger.debug('PubNub', `Download file success.`); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return (await this.sendRequest(request).then((response) => { logResponse(response); return response; })) as PlatformFile; } else throw new Error('Download File error: file sharing module disabled'); } // endregion // region Delete /** * Delete a shared file from a specific channel. * * @param parameters - Request configuration parameters. * @param callback - Request completion handler callback. */ public deleteFile( parameters: FileSharing.DeleteFileParameters, callback: ResultCallback, ): void; /** * Delete a shared file from a specific channel. * * @param parameters - Request configuration parameters. * * @returns Asynchronous delete shared file response. */ public async deleteFile(parameters: FileSharing.DeleteFileParameters): Promise; /** * Delete a shared file from a specific channel. * * @param parameters - Request configuration parameters. * @param [callback] - Request completion handler callback. * * @returns Asynchronous delete shared file response or `void` in case if `callback` provided. */ public async deleteFile( parameters: FileSharing.DeleteFileParameters, callback?: ResultCallback, ): Promise { if (process.env.FILE_SHARING_MODULE !== 'disabled') { this.logger.debug('PubNub', () => ({ messageType: 'object', message: { ...parameters }, details: `Delete file with parameters:`, })); const request = new DeleteFileRequest({ ...parameters, keySet: this._configuration.keySet }); const logResponse = (response: FileSharing.DeleteFileResponse | null) => { if (!response) return; this.logger.debug('PubNub', `Delete file success. Deleted file with ${parameters.id} ID.`); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } else throw new Error('Delete File error: file sharing module disabled'); } // endregion // endregion // -------------------------------------------------------- // ---------------------- Time API ------------------------ // -------------------------------------------------------- // region Time API /** Get current high-precision timetoken. * * @param callback - Request completion handler callback. */ public time(callback: ResultCallback): void; /** * Get current high-precision timetoken. * * @returns Asynchronous get current timetoken response. */ public async time(): Promise; /** Get current high-precision timetoken. * * @param [callback] - Request completion handler callback. * * @returns Asynchronous get current timetoken response or `void` in case if `callback` provided. */ async time(callback?: ResultCallback): Promise { this.logger.debug('PubNub', 'Get service time.'); const request = new Time.TimeRequest(); const logResponse = (response: Time.TimeResponse | null) => { if (!response) return; this.logger.debug('PubNub', `Get service time success. Current timetoken: ${response.timetoken}`); }; if (callback) return this.sendRequest(request, (status, response) => { logResponse(response); callback(status, response); }); return this.sendRequest(request).then((response) => { logResponse(response); return response; }); } // endregion // -------------------------------------------------------- // -------------------- Event emitter --------------------- // -------------------------------------------------------- // region Event emitter /** * Emit received a status update. * * Use global and local event dispatchers to deliver a status object. * * @param status - Status object which should be emitted through the listeners. * * @internal */ emitStatus(status: Status | StatusEvent) { if (process.env.SUBSCRIBE_MODULE !== 'disabled') this.eventDispatcher?.handleStatus(status); } /** * Emit receiver real-time event. * * Use global and local event dispatchers to deliver an event object. * * @param cursor - Next subscription loop timetoken. * @param event - Event object which should be emitted through the listeners. * * @internal */ private emitEvent(cursor: Subscription.SubscriptionCursor, event: Subscription.SubscriptionResponse['messages'][0]) { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { if (this._globalSubscriptionSet) this._globalSubscriptionSet.handleEvent(cursor, event); this.eventDispatcher?.handleEvent(event); Object.values(this.eventHandleCapable).forEach((eventHandleCapable) => { if (eventHandleCapable !== this._globalSubscriptionSet) eventHandleCapable.handleEvent(cursor, event); }); } } /** * Set a connection status change event handler. * * @param listener - Listener function, which will be called each time when the connection status changes. */ set onStatus(listener: ((status: Status | StatusEvent) => void) | undefined) { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { if (this.eventDispatcher) this.eventDispatcher.onStatus = listener; } else throw new Error('Listener error: subscription module disabled'); } /** * Set a new message handler. * * @param listener - Listener function, which will be called each time when a new message * is received from the real-time network. */ set onMessage(listener: ((event: Subscription.Message) => void) | undefined) { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { if (this.eventDispatcher) this.eventDispatcher.onMessage = listener; } else throw new Error('Listener error: subscription module disabled'); } /** * Set a new presence events handler. * * @param listener - Listener function, which will be called each time when a new * presence event is received from the real-time network. */ set onPresence(listener: ((event: Subscription.Presence) => void) | undefined) { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { if (this.eventDispatcher) this.eventDispatcher.onPresence = listener; } else throw new Error('Listener error: subscription module disabled'); } /** * Set a new signal handler. * * @param listener - Listener function, which will be called each time when a new signal * is received from the real-time network. */ set onSignal(listener: ((event: Subscription.Signal) => void) | undefined) { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { if (this.eventDispatcher) this.eventDispatcher.onSignal = listener; } else throw new Error('Listener error: subscription module disabled'); } /** * Set a new app context event handler. * * @param listener - Listener function, which will be called each time when a new * app context event is received from the real-time network. */ set onObjects(listener: ((event: Subscription.AppContextObject) => void) | undefined) { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { if (this.eventDispatcher) this.eventDispatcher.onObjects = listener; } else throw new Error('Listener error: subscription module disabled'); } /** * Set a new message reaction event handler. * * @param listener - Listener function, which will be called each time when a * new message reaction event is received from the real-time network. */ set onMessageAction(listener: ((event: Subscription.MessageAction) => void) | undefined) { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { if (this.eventDispatcher) this.eventDispatcher.onMessageAction = listener; } else throw new Error('Listener error: subscription module disabled'); } /** * Set a new file handler. * * @param listener - Listener function, which will be called each time when a new file * is received from the real-time network. */ set onFile(listener: ((event: Subscription.File) => void) | undefined) { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { if (this.eventDispatcher) this.eventDispatcher.onFile = listener; } else throw new Error('Listener error: subscription module disabled'); } /** * Set events handler. * * @param listener - Events listener configuration object, which lets specify handlers for multiple * types of events. */ addListener(listener: Listener) { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { if (this.eventDispatcher) { this.eventDispatcher.addListener(listener); } } else throw new Error('Listener error: subscription module disabled'); } /** * Remove real-time event listener. * * @param listener - Event listener configuration, which should be removed from the list of notified * listeners. **Important:** Should be the same object which has been passed to the * {@link addListener}. */ public removeListener(listener: Listener): void { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { if (this.eventDispatcher) this.eventDispatcher.removeListener(listener); } else throw new Error('Listener error: subscription module disabled'); } /** * Clear all real-time event listeners. */ public removeAllListeners(): void { if (process.env.SUBSCRIBE_MODULE !== 'disabled') { if (this.eventDispatcher) this.eventDispatcher.removeAllListeners(); } else throw new Error('Listener error: subscription module disabled'); } // endregion // -------------------------------------------------------- // ------------------ Cryptography API -------------------- // -------------------------------------------------------- // region Cryptography // region Common /** * Encrypt data. * * @param data - Stringified data which should be encrypted using `CryptoModule`. * @param [customCipherKey] - Cipher key which should be used to encrypt data. **Deprecated:** * use {@link Configuration#cryptoModule|cryptoModule} instead. * * @returns Data encryption result as a string. * * @deprecated Use {@link Configuration#cryptoModule|cryptoModule} instead. */ public encrypt(data: string | Payload, customCipherKey?: string): string { this.logger.warn('PubNub', "'encrypt' is deprecated. Use cryptoModule instead."); const cryptoModule = this._configuration.getCryptoModule(); if (!customCipherKey && cryptoModule && typeof data === 'string') { const encrypted = cryptoModule.encrypt(data); return typeof encrypted === 'string' ? encrypted : encode(encrypted); } if (!this.crypto) throw new Error('Encryption error: cypher key not set'); if (process.env.CRYPTO_MODULE !== 'disabled') { return this.crypto.encrypt(data, customCipherKey); } else throw new Error('Encryption error: crypto module disabled'); } /** * Decrypt data. * * @param data - Stringified data which should be encrypted using `CryptoModule`. * @param [customCipherKey] - Cipher key which should be used to decrypt data. **Deprecated:** * use {@link Configuration#cryptoModule|cryptoModule} instead. * * @returns Data decryption result as an object. * * @deprecated Use {@link Configuration#cryptoModule|cryptoModule} instead. */ public decrypt(data: string, customCipherKey?: string): Payload | null { this.logger.warn('PubNub', "'decrypt' is deprecated. Use cryptoModule instead."); const cryptoModule = this._configuration.getCryptoModule(); if (!customCipherKey && cryptoModule) { const decrypted = cryptoModule.decrypt(data); return decrypted instanceof ArrayBuffer ? JSON.parse(new TextDecoder().decode(decrypted)) : decrypted; } if (!this.crypto) throw new Error('Decryption error: cypher key not set'); if (process.env.CRYPTO_MODULE !== 'disabled') { return this.crypto.decrypt(data, customCipherKey); } else throw new Error('Decryption error: crypto module disabled'); } // endregion // region File /** * Encrypt file content. * * @param file - File which should be encrypted using `CryptoModule`. * * @returns Asynchronous file encryption result. * * @throws Error if source file isn't provided. * @throws File constructor not provided. * @throws Crypto module is missing (if non-legacy flow used). * * @deprecated Use {@link Configuration#cryptoModule|cryptoModule} instead. */ public async encryptFile(file: PubNubFileInterface): Promise; /** * Encrypt file content. * * @param key - Cipher key which should be used to encrypt data. * @param file - File which should be encrypted using legacy cryptography. * * @returns Asynchronous file encryption result. * * @throws Error if source file isn't provided. * @throws File constructor not provided. * @throws Crypto module is missing (if non-legacy flow used). * * @deprecated Use {@link Configuration#cryptoModule|cryptoModule} instead. */ public async encryptFile(key: string, file: PubNubFileInterface): Promise; /** * Encrypt file content. * * @param keyOrFile - Cipher key which should be used to encrypt data or file which should be * encrypted using `CryptoModule`. * @param [file] - File which should be encrypted using legacy cryptography. * * @returns Asynchronous file encryption result. * * @throws Error if a source file isn't provided. * @throws File constructor not provided. * @throws Crypto module is missing (if non-legacy flow used). * * @deprecated Use {@link Configuration#cryptoModule|cryptoModule} instead. */ public async encryptFile(keyOrFile: string | PubNubFileInterface, file?: PubNubFileInterface) { if (typeof keyOrFile !== 'string') file = keyOrFile; if (!file) throw new Error('File encryption error. Source file is missing.'); if (!this._configuration.PubNubFile) throw new Error('File encryption error. File constructor not configured.'); if (typeof keyOrFile !== 'string' && !this._configuration.getCryptoModule()) throw new Error('File encryption error. Crypto module not configured.'); if (typeof keyOrFile === 'string') { if (!this.cryptography) throw new Error('File encryption error. File encryption not available'); if (process.env.FILE_SHARING_MODULE !== 'disabled') return this.cryptography.encryptFile(keyOrFile, file, this._configuration.PubNubFile); else throw new Error('Encryption error: file sharing module disabled'); } if (process.env.FILE_SHARING_MODULE !== 'disabled') return this._configuration.getCryptoModule()?.encryptFile(file, this._configuration.PubNubFile); else throw new Error('Encryption error: file sharing module disabled'); } /** * Decrypt file content. * * @param file - File which should be decrypted using legacy cryptography. * * @returns Asynchronous file decryption result. * * @throws Error if source file isn't provided. * @throws File constructor not provided. * @throws Crypto module is missing (if non-legacy flow used). * * @deprecated Use {@link Configuration#cryptoModule|cryptoModule} instead. */ public async decryptFile(file: PubNubFileInterface): Promise; /** * Decrypt file content. * * @param key - Cipher key which should be used to decrypt data. * @param [file] - File which should be decrypted using legacy cryptography. * * @returns Asynchronous file decryption result. * * @throws Error if source file isn't provided. * @throws File constructor not provided. * @throws Crypto module is missing (if non-legacy flow used). * * @deprecated Use {@link Configuration#cryptoModule|cryptoModule} instead. */ public async decryptFile(key: string | PubNubFileInterface, file?: PubNubFileInterface): Promise; /** * Decrypt file content. * * @param keyOrFile - Cipher key which should be used to decrypt data or file which should be * decrypted using `CryptoModule`. * @param [file] - File which should be decrypted using legacy cryptography. * * @returns Asynchronous file decryption result. * * @throws Error if source file isn't provided. * @throws File constructor not provided. * @throws Crypto module is missing (if non-legacy flow used). * * @deprecated Use {@link Configuration#cryptoModule|cryptoModule} instead. */ public async decryptFile(keyOrFile: string | PubNubFileInterface, file?: PubNubFileInterface) { if (typeof keyOrFile !== 'string') file = keyOrFile; if (!file) throw new Error('File encryption error. Source file is missing.'); if (!this._configuration.PubNubFile) throw new Error('File decryption error. File constructor' + ' not configured.'); if (typeof keyOrFile === 'string' && !this._configuration.getCryptoModule()) throw new Error('File decryption error. Crypto module not configured.'); if (typeof keyOrFile === 'string') { if (!this.cryptography) throw new Error('File decryption error. File decryption not available'); if (process.env.FILE_SHARING_MODULE !== 'disabled') return this.cryptography.decryptFile(keyOrFile, file, this._configuration.PubNubFile); else throw new Error('Decryption error: file sharing module disabled'); } if (process.env.FILE_SHARING_MODULE !== 'disabled') return this._configuration.getCryptoModule()?.decryptFile(file, this._configuration.PubNubFile); else throw new Error('Decryption error: file sharing module disabled'); } // endregion // endregion }