UNPKG

pubnub

Version:

Publish & Subscribe Real-time Messaging with PubNub

1,243 lines (1,226 loc) 240 kB
(function (factory) { typeof define === 'function' && define.amd ? define(factory) : factory(); })((function () { 'use strict'; /** * Type with events which is emitted by PubNub client and can be handled with callback passed to the * {@link EventTarget#addEventListener|addEventListener}. */ var PubNubClientEvent; (function (PubNubClientEvent) { /** * Client unregistered (no connection through SharedWorker connection ports). * */ PubNubClientEvent["Unregister"] = "unregister"; /** * Client temporarily disconnected. */ PubNubClientEvent["Disconnect"] = "disconnect"; /** * User ID for current PubNub client has been changed. * * On identity change for proper further operation expected following actions: * - send immediate heartbeat with new `user ID` (if has been sent before) */ PubNubClientEvent["IdentityChange"] = "identityChange"; /** * Authentication token change event. * * On authentication token change for proper further operation expected following actions: * - cached `heartbeat` request query parameter updated */ PubNubClientEvent["AuthChange"] = "authChange"; /** * Presence heartbeat interval change event. * * On heartbeat interval change for proper further operation expected following actions: * - restart _backup_ heartbeat timer with new interval. */ PubNubClientEvent["HeartbeatIntervalChange"] = "heartbeatIntervalChange"; /** * `userId` presence data change event. * * On presence state change for proper further operation expected following actions: * - cached `subscribe` request query parameter updated * - cached `heartbeat` request query parameter updated */ PubNubClientEvent["PresenceStateChange"] = "presenceStateChange"; /** * Core PubNub client module request to send `subscribe` request. */ PubNubClientEvent["SendSubscribeRequest"] = "sendSubscribeRequest"; /** * Core PubNub client module request to _cancel_ specific `subscribe` request. */ PubNubClientEvent["CancelSubscribeRequest"] = "cancelSubscribeRequest"; /** * Core PubNub client module request to send `heartbeat` request. */ PubNubClientEvent["SendHeartbeatRequest"] = "sendHeartbeatRequest"; /** * Core PubNub client module request to send `leave` request. */ PubNubClientEvent["SendLeaveRequest"] = "sendLeaveRequest"; })(PubNubClientEvent || (PubNubClientEvent = {})); /** * Base request processing event class. */ class BasePubNubClientEvent extends CustomEvent { /** * Retrieve reference to PubNub client which dispatched event. * * @returns Reference to PubNub client which dispatched event. */ get client() { return this.detail.client; } } /** * Dispatched by PubNub client when it has been unregistered. */ class PubNubClientUnregisterEvent extends BasePubNubClientEvent { /** * Create PubNub client unregister event. * * @param client - Reference to unregistered PubNub client. */ constructor(client) { super(PubNubClientEvent.Unregister, { detail: { client } }); } /** * Create a clone of `unregister` event to make it possible to forward event upstream. * * @returns Clone of `unregister` event. */ clone() { return new PubNubClientUnregisterEvent(this.client); } } /** * Dispatched by PubNub client when it has been disconnected. */ class PubNubClientDisconnectEvent extends BasePubNubClientEvent { /** * Create PubNub client disconnect event. * * @param client - Reference to disconnected PubNub client. */ constructor(client) { super(PubNubClientEvent.Disconnect, { detail: { client } }); } /** * Create a clone of `disconnect` event to make it possible to forward event upstream. * * @returns Clone of `disconnect` event. */ clone() { return new PubNubClientDisconnectEvent(this.client); } } /** * Dispatched by PubNub client when it changes user identity (`userId` has been changed). */ class PubNubClientIdentityChangeEvent extends BasePubNubClientEvent { /** * Create PubNub client identity change event. * * @param client - Reference to the PubNub client which changed identity. * @param oldUserId - User ID which has been previously used by the `client`. * @param newUserId - User ID which will used by the `client`. */ constructor(client, oldUserId, newUserId) { super(PubNubClientEvent.IdentityChange, { detail: { client, oldUserId, newUserId } }); } /** * Retrieve `userId` which has been previously used by the `client`. * * @returns `userId` which has been previously used by the `client`. */ get oldUserId() { return this.detail.oldUserId; } /** * Retrieve `userId` which will used by the `client`. * * @returns `userId` which will used by the `client`. */ get newUserId() { return this.detail.newUserId; } /** * Create a clone of `identity` _change_ event to make it possible to forward event upstream. * * @returns Clone of `identity` _change_ event. */ clone() { return new PubNubClientIdentityChangeEvent(this.client, this.oldUserId, this.newUserId); } } /** * Dispatched by PubNub client when it changes authentication data (`auth`) has been changed. */ class PubNubClientAuthChangeEvent extends BasePubNubClientEvent { /** * Create PubNub client authentication change event. * * @param client - Reference to the PubNub client which changed authentication. * @param [newAuth] - Authentication which will used by the `client`. * @param [oldAuth] - Authentication which has been previously used by the `client`. */ constructor(client, newAuth, oldAuth) { super(PubNubClientEvent.AuthChange, { detail: { client, oldAuth, newAuth } }); } /** * Retrieve authentication which has been previously used by the `client`. * * @returns Authentication which has been previously used by the `client`. */ get oldAuth() { return this.detail.oldAuth; } /** * Retrieve authentication which will used by the `client`. * * @returns Authentication which will used by the `client`. */ get newAuth() { return this.detail.newAuth; } /** * Create a clone of `authentication` _change_ event to make it possible to forward event upstream. * * @returns Clone `authentication` _change_ event. */ clone() { return new PubNubClientAuthChangeEvent(this.client, this.newAuth, this.oldAuth); } } /** * Dispatched by PubNub client when it changes heartbeat interval. */ class PubNubClientHeartbeatIntervalChangeEvent extends BasePubNubClientEvent { /** * Create PubNub client heartbeat interval change event. * * @param client - Reference to the PubNub client which changed heartbeat interval. * @param [newInterval] - New heartbeat request send interval. * @param [oldInterval] - Previous heartbeat request send interval. */ constructor(client, newInterval, oldInterval) { super(PubNubClientEvent.HeartbeatIntervalChange, { detail: { client, oldInterval, newInterval } }); } /** * Retrieve previous heartbeat request send interval. * * @returns Previous heartbeat request send interval. */ get oldInterval() { return this.detail.oldInterval; } /** * Retrieve new heartbeat request send interval. * * @returns New heartbeat request send interval. */ get newInterval() { return this.detail.newInterval; } /** * Create a clone of the `heartbeat interval` _change_ event to make it possible to forward the event upstream. * * @returns Clone of `heartbeat interval` _change_ event. */ clone() { return new PubNubClientHeartbeatIntervalChangeEvent(this.client, this.newInterval, this.oldInterval); } } /** * Dispatched by PubNub client when presence state for its user has been changed. */ class PubNubClientPresenceStateChangeEvent extends BasePubNubClientEvent { /** * Create a PubNub client presence state change event. * * @param client - Reference to the PubNub client that changed presence state for `userId`. * @param state - Payloads that are associated with `userId` at specified (as keys) channels and groups. */ constructor(client, state) { super(PubNubClientEvent.PresenceStateChange, { detail: { client, state } }); } /** * Retrieve the presence state that has been associated with `client`'s `userId`. * * @returns Presence state that has been associated with `client`'s `userId */ get state() { return this.detail.state; } /** * Create a clone of `presence state` _change_ event to make it possible to forward event upstream. * * @returns Clone `presence state` _change_ event. */ clone() { return new PubNubClientPresenceStateChangeEvent(this.client, this.state); } } /** * Dispatched when the core PubNub client module requested to _send_ a `subscribe` request. */ class PubNubClientSendSubscribeEvent extends BasePubNubClientEvent { /** * Create subscribe request send event. * * @param client - Reference to the PubNub client which requested to send request. * @param request - Subscription request object. */ constructor(client, request) { super(PubNubClientEvent.SendSubscribeRequest, { detail: { client, request } }); } /** * Retrieve subscription request object. * * @returns Subscription request object. */ get request() { return this.detail.request; } /** * Create clone of _send_ `subscribe` request event to make it possible to forward event upstream. * * @returns Clone of _send_ `subscribe` request event. */ clone() { return new PubNubClientSendSubscribeEvent(this.client, this.request); } } /** * Dispatched when the core PubNub client module requested to _cancel_ `subscribe` request. */ class PubNubClientCancelSubscribeEvent extends BasePubNubClientEvent { /** * Create `subscribe` request _cancel_ event. * * @param client - Reference to the PubNub client which requested to _send_ request. * @param request - Subscription request object. */ constructor(client, request) { super(PubNubClientEvent.CancelSubscribeRequest, { detail: { client, request } }); } /** * Retrieve subscription request object. * * @returns Subscription request object. */ get request() { return this.detail.request; } /** * Create clone of _cancel_ `subscribe` request event to make it possible to forward event upstream. * * @returns Clone of _cancel_ `subscribe` request event. */ clone() { return new PubNubClientCancelSubscribeEvent(this.client, this.request); } } /** * Dispatched when the core PubNub client module requested to _send_ `heartbeat` request. */ class PubNubClientSendHeartbeatEvent extends BasePubNubClientEvent { /** * Create `heartbeat` request _send_ event. * * @param client - Reference to the PubNub client which requested to send request. * @param request - Heartbeat request object. */ constructor(client, request) { super(PubNubClientEvent.SendHeartbeatRequest, { detail: { client, request } }); } /** * Retrieve heartbeat request object. * * @returns Heartbeat request object. */ get request() { return this.detail.request; } /** * Create clone of _send_ `heartbeat` request event to make it possible to forward event upstream. * * @returns Clone of _send_ `heartbeat` request event. */ clone() { return new PubNubClientSendHeartbeatEvent(this.client, this.request); } } /** * Dispatched when the core PubNub client module requested to _send_ `leave` request. */ class PubNubClientSendLeaveEvent extends BasePubNubClientEvent { /** * Create `leave` request _send_ event. * * @param client - Reference to the PubNub client which requested to send request. * @param request - Leave request object. */ constructor(client, request) { super(PubNubClientEvent.SendLeaveRequest, { detail: { client, request } }); } /** * Retrieve leave request object. * * @returns Leave request object. */ get request() { return this.detail.request; } /** * Create clone of _send_ `leave` request event to make it possible to forward event upstream. * * @returns Clone of _send_ `leave` request event. */ clone() { return new PubNubClientSendLeaveEvent(this.client, this.request); } } /** * Type with events which is dispatched by PubNub clients manager and can be handled with callback passed to the * {@link EventTarget#addEventListener|addEventListener}. */ var PubNubClientsManagerEvent; (function (PubNubClientsManagerEvent) { /** * New PubNub client has been registered. */ PubNubClientsManagerEvent["Registered"] = "Registered"; /** * PubNub client has been unregistered. */ PubNubClientsManagerEvent["Unregistered"] = "Unregistered"; })(PubNubClientsManagerEvent || (PubNubClientsManagerEvent = {})); /** * Dispatched by clients manager when new PubNub client registers within `SharedWorker`. */ class PubNubClientManagerRegisterEvent extends CustomEvent { /** * Create client registration event. * * @param client - Reference to the registered PubNub client. */ constructor(client) { super(PubNubClientsManagerEvent.Registered, { detail: client }); } /** * Retrieve reference to registered PubNub client. * * @returns Reference to registered PubNub client. */ get client() { return this.detail; } /** * Create clone of new client register event to make it possible to forward event upstream. * * @returns Client new client register event. */ clone() { return new PubNubClientManagerRegisterEvent(this.client); } } /** * Dispatched by clients manager when PubNub client unregisters from `SharedWorker`. */ class PubNubClientManagerUnregisterEvent extends CustomEvent { /** * Create client unregistration event. * * @param client - Reference to the unregistered PubNub client. * @param withLeave - Whether `leave` request should be sent or not. */ constructor(client, withLeave = false) { super(PubNubClientsManagerEvent.Unregistered, { detail: { client, withLeave } }); } /** * Retrieve reference to the unregistered PubNub client. * * @returns Reference to the unregistered PubNub client. */ get client() { return this.detail.client; } /** * Retrieve whether `leave` request should be sent or not. * * @returns `true` if `leave` request should be sent for previously used channels and groups. */ get withLeave() { return this.detail.withLeave; } /** * Create clone of client unregister event to make it possible to forward event upstream. * * @returns Client client unregister event. */ clone() { return new PubNubClientManagerUnregisterEvent(this.client, this.withLeave); } } /** * Type with events which is dispatched by subscription state in response to client-provided requests and PubNub * client state change. */ var SubscriptionStateEvent; (function (SubscriptionStateEvent) { /** * Subscription state has been changed. */ SubscriptionStateEvent["Changed"] = "changed"; /** * Subscription state has been invalidated after all clients' state was removed from it. */ SubscriptionStateEvent["Invalidated"] = "invalidated"; })(SubscriptionStateEvent || (SubscriptionStateEvent = {})); /** * Dispatched by subscription state when state and service requests are changed. */ class SubscriptionStateChangeEvent extends CustomEvent { /** * Create subscription state change event. * * @param withInitialResponse - List of initial `client`-provided {@link SubscribeRequest|subscribe} requests with * timetokens and regions that should be returned right away. * @param newRequests - List of new service requests which need to be scheduled for processing. * @param canceledRequests - List of previously scheduled service requests which should be cancelled. * @param leaveRequest - Request which should be used to announce `leave` from part of the channels and groups. */ constructor(withInitialResponse, newRequests, canceledRequests, leaveRequest) { super(SubscriptionStateEvent.Changed, { detail: { withInitialResponse, newRequests, canceledRequests, leaveRequest }, }); } /** * Retrieve list of initial `client`-provided {@link SubscribeRequest|subscribe} requests with timetokens and regions * that should be returned right away. * * @returns List of initial `client`-provided {@link SubscribeRequest|subscribe} requests with timetokens and regions * that should be returned right away. */ get requestsWithInitialResponse() { return this.detail.withInitialResponse; } /** * Retrieve list of new service requests which need to be scheduled for processing. * * @returns List of new service requests which need to be scheduled for processing. */ get newRequests() { return this.detail.newRequests; } /** * Retrieve request which should be used to announce `leave` from part of the channels and groups. * * @returns Request which should be used to announce `leave` from part of the channels and groups. */ get leaveRequest() { return this.detail.leaveRequest; } /** * Retrieve list of previously scheduled service requests which should be cancelled. * * @returns List of previously scheduled service requests which should be cancelled. */ get canceledRequests() { return this.detail.canceledRequests; } /** * Create clone of subscription state change event to make it possible to forward event upstream. * * @returns Client subscription state change event. */ clone() { return new SubscriptionStateChangeEvent(this.requestsWithInitialResponse, this.newRequests, this.canceledRequests, this.leaveRequest); } } /** * Dispatched by subscription state when it has been invalidated. */ class SubscriptionStateInvalidateEvent extends CustomEvent { /** * Create subscription state invalidation event. */ constructor() { super(SubscriptionStateEvent.Invalidated); } /** * Create clone of subscription state change event to make it possible to forward event upstream. * * @returns Client subscription state change event. */ clone() { return new SubscriptionStateInvalidateEvent(); } } /** * Type with events which is emitted by request and can be handled with callback passed to the * {@link EventTarget#addEventListener|addEventListener}. */ var PubNubSharedWorkerRequestEvents; (function (PubNubSharedWorkerRequestEvents) { /** * Request processing started. */ PubNubSharedWorkerRequestEvents["Started"] = "started"; /** * Request processing has been canceled. * * **Note:** This event dispatched only by client-provided requests. */ PubNubSharedWorkerRequestEvents["Canceled"] = "canceled"; /** * Request successfully completed. */ PubNubSharedWorkerRequestEvents["Success"] = "success"; /** * Request completed with error. * * Error can be caused by: * - missing permissions (403) * - network issues */ PubNubSharedWorkerRequestEvents["Error"] = "error"; })(PubNubSharedWorkerRequestEvents || (PubNubSharedWorkerRequestEvents = {})); /** * Base request processing event class. */ class BaseRequestEvent extends CustomEvent { /** * Retrieve service (aggregated / updated) request. * * @returns Service (aggregated / updated) request. */ get request() { return this.detail.request; } } /** * Dispatched by request when linked service request processing started. */ class RequestStartEvent extends BaseRequestEvent { /** * Create request processing start event. * * @param request - Service (aggregated / updated) request. */ constructor(request) { super(PubNubSharedWorkerRequestEvents.Started, { detail: { request } }); } /** * Create clone of request processing start event to make it possible to forward event upstream. * * @param request - Custom requests with this event should be cloned. * @returns Client request processing start event. */ clone(request) { return new RequestStartEvent(request !== null && request !== void 0 ? request : this.request); } } /** * Dispatched by request when linked service request processing completed. */ class RequestSuccessEvent extends BaseRequestEvent { /** * Create request processing success event. * * @param request - Service (aggregated / updated) request. * @param fetchRequest - Actual request which has been used with {@link fetch}. * @param response - PubNub service response. */ constructor(request, fetchRequest, response) { super(PubNubSharedWorkerRequestEvents.Success, { detail: { request, fetchRequest, response } }); } /** * Retrieve actual request which has been used with {@link fetch}. * * @returns Actual request which has been used with {@link fetch}. */ get fetchRequest() { return this.detail.fetchRequest; } /** * Retrieve PubNub service response. * * @returns Service response. */ get response() { return this.detail.response; } /** * Create clone of request processing success event to make it possible to forward event upstream. * * @param request - Custom requests with this event should be cloned. * @returns Client request processing success event. */ clone(request) { return new RequestSuccessEvent(request !== null && request !== void 0 ? request : this.request, request ? request.asFetchRequest : this.fetchRequest, this.response); } } /** * Dispatched by request when linked service request processing failed / service error response. */ class RequestErrorEvent extends BaseRequestEvent { /** * Create request processing error event. * * @param request - Service (aggregated / updated) request. * @param fetchRequest - Actual request which has been used with {@link fetch}. * @param error - Request processing error information. */ constructor(request, fetchRequest, error) { super(PubNubSharedWorkerRequestEvents.Error, { detail: { request, fetchRequest, error } }); } /** * Retrieve actual request which has been used with {@link fetch}. * * @returns Actual request which has been used with {@link fetch}. */ get fetchRequest() { return this.detail.fetchRequest; } /** * Retrieve request processing error description. * * @returns Request processing error description. */ get error() { return this.detail.error; } /** * Create clone of request processing failure event to make it possible to forward event upstream. * * @param request - Custom requests with this event should be cloned. * @returns Client request processing failure event. */ clone(request) { return new RequestErrorEvent(request !== null && request !== void 0 ? request : this.request, request ? request.asFetchRequest : this.fetchRequest, this.error); } } /** * Dispatched by request when it has been canceled. */ class RequestCancelEvent extends BaseRequestEvent { /** * Create request cancelling event. * * @param request - Client-provided (original) request. */ constructor(request) { super(PubNubSharedWorkerRequestEvents.Canceled, { detail: { request } }); } /** * Create clone of request cancel event to make it possible to forward event upstream. * * @param request - Custom requests with this event should be cloned. * @returns Client request cancel event. */ clone(request) { return new RequestCancelEvent(request !== null && request !== void 0 ? request : this.request); } } var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function getDefaultExportFromCjs (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } var uuid = {exports: {}}; /*! lil-uuid - v0.1 - MIT License - https://github.com/lil-js/uuid */ uuid.exports; (function (module, exports) { (function (root, factory) { { factory(exports); if (module !== null) { module.exports = exports.uuid; } } }(commonjsGlobal, function (exports) { var VERSION = '0.1.0'; var uuidRegex = { '3': /^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i, '4': /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i, '5': /^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i, all: /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i }; function uuid() { var uuid = '', i, random; for (i = 0; i < 32; i++) { random = Math.random() * 16 | 0; if (i === 8 || i === 12 || i === 16 || i === 20) uuid += '-'; uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random)).toString(16); } return uuid } function isUUID(str, version) { var pattern = uuidRegex[version || 'all']; return pattern && pattern.test(str) || false } uuid.isUUID = isUUID; uuid.VERSION = VERSION; exports.uuid = uuid; exports.isUUID = isUUID; })); } (uuid, uuid.exports)); var uuidExports = uuid.exports; var uuidGenerator$1 = /*@__PURE__*/getDefaultExportFromCjs(uuidExports); /** * Random identifier generator helper module. * * @internal */ /** @internal */ var uuidGenerator = { createUUID() { if (uuidGenerator$1.uuid) { return uuidGenerator$1.uuid(); } // @ts-expect-error Depending on module type it may be callable. return uuidGenerator$1(); }, }; /** * Base shared worker request implementation. * * In the `SharedWorker` context, this base class is used both for `client`-provided (they won't be used for actual * request) and those that are created by `SharedWorker` code (`service` request, which will be used in actual * requests). * * **Note:** The term `service` request in inline documentation will mean request created by `SharedWorker` and used to * call PubNub REST API. */ class BasePubNubRequest extends EventTarget { // endregion // -------------------------------------------------------- // --------------------- Constructors --------------------- // -------------------------------------------------------- // region Constructors /** * Create request object. * * @param request - Transport request. * @param subscribeKey - Subscribe REST API access key. * @param userId - Unique user identifier from the name of which request will be made. * @param channels - List of channels used in request. * @param channelGroups - List of channel groups used in request. * @param [accessToken] - Access token with permissions to access provided `channels` and `channelGroups` on behalf of * `userId`. */ constructor(request, subscribeKey, userId, channels, channelGroups, accessToken) { super(); this.request = request; this.subscribeKey = subscribeKey; this.channels = channels; this.channelGroups = channelGroups; /** * Map of attached to the service request `client`-provided requests by their request identifiers. * * **Context:** `service`-provided requests only. */ this.dependents = {}; /** * Whether the request already received a service response or an error. * * **Important:** Any interaction with completed requests except requesting properties is prohibited. */ this._completed = false; /** * Whether request has been cancelled or not. * * **Important:** Any interaction with canceled requests except requesting properties is prohibited. */ this._canceled = false; /** * Stringify request query key/value pairs. * * @param query - Request query object. * @returns Stringified query object. */ this.queryStringFromObject = (query) => { return Object.keys(query) .map((key) => { const queryValue = query[key]; if (!Array.isArray(queryValue)) return `${key}=${this.encodeString(queryValue)}`; return queryValue.map((value) => `${key}=${this.encodeString(value)}`).join('&'); }) .join('&'); }; this._accessToken = accessToken; this._userId = userId; } // endregion // -------------------------------------------------------- // ---------------------- Properties ---------------------- // -------------------------------------------------------- // region Properties /** * Get the request's unique identifier. * * @returns Request's unique identifier. */ get identifier() { return this.request.identifier; } /** * Retrieve the origin that is used to access PubNub REST API. * * @returns Origin, which is used to access PubNub REST API. */ get origin() { return this.request.origin; } /** * Retrieve the unique user identifier from the name of which request will be made. * * @returns Unique user identifier from the name of which request will be made. */ get userId() { return this._userId; } /** * Update the unique user identifier from the name of which request will be made. * * @param value - New unique user identifier. */ set userId(value) { this._userId = value; // Patch underlying transport request query parameters to use new value. this.request.queryParameters.uuid = value; } /** * Retrieve access token with permissions to access provided `channels` and `channelGroups`. * * @returns Access token with permissions for {@link userId} or `undefined` if not set. */ get accessToken() { return this._accessToken; } /** * Update the access token which should be used to access provided `channels` and `channelGroups` by the user with * {@link userId}. * * @param [value] - Access token with permissions for {@link userId}. */ set accessToken(value) { this._accessToken = value; // Patch underlying transport request query parameters to use new value. if (value) this.request.queryParameters.auth = value.toString(); else delete this.request.queryParameters.auth; } /** * Retrieve {@link PubNubClient|PubNub} client associates with request. * * **Context:** `client`-provided requests only. * * @returns Reference to the {@link PubNubClient|PubNub} client that is sending the request. */ get client() { return this._client; } /** * Associate request with PubNub client. * * **Context:** `client`-provided requests only. * * @param value - {@link PubNubClient|PubNub} client that created request in `SharedWorker` context. */ set client(value) { this._client = value; } /** * Retrieve whether the request already received a service response or an error. * * @returns `true` if request already completed processing (not with {@link cancel}). */ get completed() { return this._completed; } /** * Retrieve whether the request can be cancelled or not. * * @returns `true` if there is a possibility and meaning to be able to cancel the request. */ get cancellable() { return this.request.cancellable; } /** * Retrieve whether the request has been canceled prior to completion or not. * * @returns `true` if the request didn't complete processing. */ get canceled() { return this._canceled; } /** * Update controller, which is used to cancel ongoing `service`-provided requests by signaling {@link fetch}. * * **Context:** `service`-provided requests only. * * @param value - Controller that has been used to signal {@link fetch} for request cancellation. */ set fetchAbortController(value) { // There is no point in completed request `fetch` abort controller set. if (this.completed || this.canceled) return; // Fetch abort controller can't be set for `client`-provided requests. if (!this.isServiceRequest) { console.error('Unexpected attempt to set fetch abort controller on client-provided request.'); return; } if (this._fetchAbortController) { console.error('Only one abort controller can be set for service-provided requests.'); return; } this._fetchAbortController = value; } /** * Retrieve `service`-provided fetch request abort controller. * * **Context:** `service`-provided requests only. * * @returns `service`-provided fetch request abort controller. */ get fetchAbortController() { return this._fetchAbortController; } /** * Represent transport request as {@link fetch} {@link Request}. * * @returns Ready-to-use {@link Request} instance. */ get asFetchRequest() { const queryParameters = this.request.queryParameters; const headers = {}; let query = ''; if (this.request.headers) for (const [key, value] of Object.entries(this.request.headers)) headers[key] = value; if (queryParameters && Object.keys(queryParameters).length !== 0) query = `?${this.queryStringFromObject(queryParameters)}`; return new Request(`${this.origin}${this.request.path}${query}`, { method: this.request.method, headers: Object.keys(headers).length ? headers : undefined, redirect: 'follow', }); } /** * Retrieve the service (aggregated/modified) request, which will actually be used to call the REST API endpoint. * * **Context:** `client`-provided requests only. * * @returns Service (aggregated/modified) request, which will actually be used to call the REST API endpoint. */ get serviceRequest() { return this._serviceRequest; } /** * Link request processing results to the service (aggregated/modified) request. * * **Context:** `client`-provided requests only. * * @param value - Service (aggregated/modified) request for which process progress should be observed. */ set serviceRequest(value) { // This function shouldn't be called even unintentionally, on the `service`-provided requests. if (this.isServiceRequest) { console.error('Unexpected attempt to set service-provided request on service-provided request.'); return; } const previousServiceRequest = this.serviceRequest; this._serviceRequest = value; // Detach from the previous service request if it has been changed (to a new one or unset). if (previousServiceRequest && (!value || previousServiceRequest.identifier !== value.identifier)) previousServiceRequest.detachRequest(this); // There is no need to set attach to service request if either of them is already completed, or canceled. if (this.completed || this.canceled || (value && (value.completed || value.canceled))) { this._serviceRequest = undefined; return; } if (previousServiceRequest && value && previousServiceRequest.identifier === value.identifier) return; // Attach the request to the service request processing results. if (value) value.attachRequest(this); } /** * Retrieve whether the receiver is a `service`-provided request or not. * * @returns `true` if the request has been created by the `SharedWorker`. */ get isServiceRequest() { return !this.client; } // endregion // -------------------------------------------------------- // ---------------------- Dependency ---------------------- // -------------------------------------------------------- // region Dependency /** * Retrieve a list of `client`-provided requests that have been attached to the `service`-provided request. * * **Context:** `service`-provided requests only. * * @returns List of attached `client`-provided requests. */ dependentRequests() { // Return an empty list for `client`-provided requests. if (!this.isServiceRequest) return []; return Object.values(this.dependents); } /** * Attach the `client`-provided request to the receiver (`service`-provided request) to receive a response from the * PubNub REST API. * * **Context:** `service`-provided requests only. * * @param request - `client`-provided request that should be attached to the receiver (`service`-provided request). */ attachRequest(request) { // Request attachments works only on service requests. if (!this.isServiceRequest || this.dependents[request.identifier]) { if (!this.isServiceRequest) console.error('Unexpected attempt to attach requests using client-provided request.'); return; } this.dependents[request.identifier] = request; this.addEventListenersForRequest(request); } /** * Detach the `client`-provided request from the receiver (`service`-provided request) to ignore any response from the * PubNub REST API. * * **Context:** `service`-provided requests only. * * @param request - `client`-provided request that should be attached to the receiver (`service`-provided request). */ detachRequest(request) { // Request detachments works only on service requests. if (!this.isServiceRequest || !this.dependents[request.identifier]) { if (!this.isServiceRequest) console.error('Unexpected attempt to detach requests using client-provided request.'); return; } delete this.dependents[request.identifier]; request.removeEventListenersFromRequest(); // Because `service`-provided requests are created in response to the `client`-provided one we need to cancel the // receiver if there are no more attached `client`-provided requests. // This ensures that there will be no abandoned/dangling `service`-provided request in `SharedWorker` structures. if (Object.keys(this.dependents).length === 0) this.cancel('Cancel request'); } // endregion // -------------------------------------------------------- // ------------------ Request processing ------------------ // -------------------------------------------------------- // region Request processing /** * Notify listeners that ongoing request processing has been cancelled. * * **Note:** The current implementation doesn't let {@link PubNubClient|PubNub} directly call * {@link cancel}, and it can be called from `SharedWorker` code logic. * * **Important:** Previously attached `client`-provided requests should be re-attached to another `service`-provided * request or properly cancelled with {@link PubNubClient|PubNub} notification of the core PubNub client module. * * @param [reason] - Reason because of which the request has been cancelled. The request manager uses this to specify * whether the `service`-provided request has been cancelled on-demand or because of timeout. * @param [notifyDependent] - Whether dependent requests should receive cancellation error or not. * @returns List of detached `client`-provided requests. */ cancel(reason, notifyDependent = false) { // There is no point in completed request cancellation. if (this.completed || this.canceled) { return []; } const dependentRequests = this.dependentRequests(); if (this.isServiceRequest) { // Detach request if not interested in receiving request cancellation error (because of timeout). // When switching between aggregated `service`-provided requests there is no need in handling cancellation of // outdated request. if (!notifyDependent) dependentRequests.forEach((request) => (request.serviceRequest = undefined)); if (this._fetchAbortController) { this._fetchAbortController.abort(reason); this._fetchAbortController = undefined; } } else this.serviceRequest = undefined; this._canceled = true; this.stopRequestTimeoutTimer(); this.dispatchEvent(new RequestCancelEvent(this)); return dependentRequests; } /** * Create and return running request processing timeout timer. * * @returns Promise with timout timer resolution. */ requestTimeoutTimer() { return new Promise((_, reject) => { this._fetchTimeoutTimer = setTimeout(() => { reject(new Error('Request timeout')); this.cancel('Cancel because of timeout', true); }, this.request.timeout * 1000); }); } /** * Stop request processing timeout timer without error. */ stopRequestTimeoutTimer() { if (!this._fetchTimeoutTimer) return; clearTimeout(this._fetchTimeoutTimer); this._fetchTimeoutTimer = undefined; } /** * Handle request processing started by the request manager (actual sending). */ handleProcessingStarted() { // Log out request processing start (will be made only for client-provided request). this.logRequestStart(this); this.dispatchEvent(new RequestStartEvent(this)); } /** * Handle request processing successfully completed by request manager (actual sending). * * @param fetchRequest - Reference to the actual request that has been used with {@link fetch}. * @param response - PubNub service response which is ready to be sent to the core PubNub client module. */ handleProcessingSuccess(fetchRequest, response) { this.addRequestInformationForResult(this, fetchRequest, response); this.logRequestSuccess(this, response); this._completed = true; this.stopRequestTimeoutTimer(); this.dispatchEvent(new RequestSuccessEvent(this, fetchRequest, response)); } /** * Handle request processing failed by request manager (actual sending). * * @param fetchRequest - Reference to the actual request that has been used with {@link fetch}. * @param error - Request processing error description. */ handleProcessingError(fetchRequest, error) { this.addRequestInformationForResult(this, fetchRequest, error); this.logRequestError(this, error); this._completed = true; this.stopRequestTimeoutTimer(); this.dispatchEvent(new RequestErrorEvent(this, fetchRequest, error)); } // endregion // -------------------------------------------------------- // ------------------- Event Handlers --------------------- // -------------------------------------------------------- // region Event handlers