pubnub
Version:
Publish & Subscribe Real-time Messaging with PubNub
1,243 lines (1,226 loc) • 240 kB
JavaScript
(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