|
2890 | 2890 | *
|
2891 | 2891 | * @param errorOrResponse - `Error` or service error response object from which error information
|
2892 | 2892 | * should be extracted.
|
2893 |
| - * @param data - Preprocessed service error response. |
| 2893 | + * @param [data] - Preprocessed service error response. |
2894 | 2894 | *
|
2895 | 2895 | * @returns `PubNubAPIError` object with known error category and additional information (if
|
2896 | 2896 | * available).
|
|
2969 | 2969 | *
|
2970 | 2970 | * @param response - Service error response object from which error information should be
|
2971 | 2971 | * extracted.
|
2972 |
| - * @param data - Preprocessed service error response. |
| 2972 | + * @param [data] - Preprocessed service error response. |
2973 | 2973 | *
|
2974 | 2974 | * @returns `PubNubAPIError` object with known error category and additional information (if
|
2975 | 2975 | * available).
|
|
2990 | 2990 | category = StatusCategory$1.PNAccessDeniedCategory;
|
2991 | 2991 | message = 'Access denied';
|
2992 | 2992 | }
|
| 2993 | + if (typeof response === 'object' && Object.keys(response).length === 0) { |
| 2994 | + category = StatusCategory$1.PNMalformedResponseCategory; |
| 2995 | + message = 'Malformed response (network issues)'; |
| 2996 | + status = 400; |
| 2997 | + } |
2993 | 2998 | // Try to get more information about error from service response.
|
2994 | 2999 | if (data && data.byteLength > 0) {
|
2995 | 3000 | const decoded = new TextDecoder().decode(data);
|
|
3042 | 3047 | * @param message - Short API call error description.
|
3043 | 3048 | * @param category - Error category.
|
3044 | 3049 | * @param statusCode - Response HTTP status code.
|
3045 |
| - * @param errorData - Error information. |
| 3050 | + * @param [errorData] - Error information. |
3046 | 3051 | */
|
3047 | 3052 | constructor(message, category, statusCode, errorData) {
|
3048 | 3053 | super(message);
|
|
3065 | 3070 | operation,
|
3066 | 3071 | statusCode: this.statusCode,
|
3067 | 3072 | errorData: this.errorData,
|
| 3073 | + // @ts-expect-error Inner helper for JSON.stringify. |
| 3074 | + toJSON: function () { |
| 3075 | + let normalizedErrorData; |
| 3076 | + const errorData = this.errorData; |
| 3077 | + if (errorData) { |
| 3078 | + try { |
| 3079 | + if (typeof errorData === 'object') { |
| 3080 | + const errorObject = Object.assign(Object.assign(Object.assign(Object.assign({}, ('name' in errorData ? { name: errorData.name } : {})), ('message' in errorData ? { message: errorData.message } : {})), ('stack' in errorData ? { stack: errorData.stack } : {})), errorData); |
| 3081 | + normalizedErrorData = JSON.parse(JSON.stringify(errorObject, PubNubAPIError.circularReplacer())); |
| 3082 | + } |
| 3083 | + else |
| 3084 | + normalizedErrorData = errorData; |
| 3085 | + } |
| 3086 | + catch (_) { |
| 3087 | + normalizedErrorData = { error: 'Could not serialize the error object' }; |
| 3088 | + } |
| 3089 | + } |
| 3090 | + // Make sure to exclude `toJSON` function from the final object. |
| 3091 | + const _a = this, status = __rest(_a, ["toJSON"]); |
| 3092 | + return JSON.stringify(Object.assign(Object.assign({}, status), { errorData: normalizedErrorData })); |
| 3093 | + }, |
3068 | 3094 | };
|
3069 | 3095 | }
|
3070 | 3096 | /**
|
3071 | 3097 | * Convert API error object to PubNub client error object.
|
3072 | 3098 | *
|
3073 | 3099 | * @param operation - Request operation during which error happened.
|
3074 |
| - * @param message - Custom error message. |
| 3100 | + * @param [message] - Custom error message. |
3075 | 3101 | *
|
3076 | 3102 | * @returns Client-facing pre-formatted endpoint call error.
|
3077 | 3103 | */
|
3078 | 3104 | toPubNubError(operation, message) {
|
3079 | 3105 | return new PubNubError(message !== null && message !== void 0 ? message : this.message, this.toStatus(operation));
|
3080 | 3106 | }
|
| 3107 | + /** |
| 3108 | + * Function which handles circular references in serialized JSON. |
| 3109 | + * |
| 3110 | + * @returns Circular reference replacer function. |
| 3111 | + * |
| 3112 | + * @internal |
| 3113 | + */ |
| 3114 | + static circularReplacer() { |
| 3115 | + const visited = new WeakSet(); |
| 3116 | + return function (_, value) { |
| 3117 | + if (typeof value === 'object' && value !== null) { |
| 3118 | + if (visited.has(value)) |
| 3119 | + return '[Circular]'; |
| 3120 | + visited.add(value); |
| 3121 | + } |
| 3122 | + return value; |
| 3123 | + }; |
| 3124 | + } |
3081 | 3125 | }
|
3082 | 3126 |
|
3083 | 3127 | /**
|
|
3759 | 3803 | return base.PubNubFile;
|
3760 | 3804 | },
|
3761 | 3805 | get version() {
|
3762 |
| - return '8.9.0'; |
| 3806 | + return '8.9.1'; |
3763 | 3807 | },
|
3764 | 3808 | getVersion() {
|
3765 | 3809 | return this.version;
|
|
4273 | 4317 | let fetchError = error;
|
4274 | 4318 | if (typeof error === 'string') {
|
4275 | 4319 | const errorMessage = error.toLowerCase();
|
4276 |
| - if (errorMessage.includes('timeout') || !errorMessage.includes('cancel')) |
4277 |
| - fetchError = new Error(error); |
4278 |
| - else if (errorMessage.includes('cancel')) |
4279 |
| - fetchError = new DOMException('Aborted', 'AbortError'); |
| 4320 | + fetchError = new Error(error); |
| 4321 | + if (!errorMessage.includes('timeout') && errorMessage.includes('cancel')) |
| 4322 | + fetchError.name = 'AbortError'; |
4280 | 4323 | }
|
4281 | 4324 | throw PubNubAPIError.create(fetchError);
|
4282 | 4325 | });
|
|
5036 | 5079 | if (status.category === StatusCategory$1.PNTimeoutCategory) {
|
5037 | 5080 | this.startSubscribeLoop();
|
5038 | 5081 | }
|
5039 |
| - else if (status.category === StatusCategory$1.PNNetworkIssuesCategory) { |
| 5082 | + else if (status.category === StatusCategory$1.PNNetworkIssuesCategory || |
| 5083 | + status.category === StatusCategory$1.PNMalformedResponseCategory) { |
5040 | 5084 | this.disconnect();
|
5041 | 5085 | if (status.error && this.configuration.autoNetworkDetection && this.isOnline) {
|
5042 | 5086 | this.isOnline = false;
|
|
5058 | 5102 | this.listenerManager.announceStatus(reconnectedAnnounce);
|
5059 | 5103 | });
|
5060 | 5104 | this.reconnectionManager.startPolling();
|
5061 |
| - this.listenerManager.announceStatus(status); |
| 5105 | + this.listenerManager.announceStatus(Object.assign(Object.assign({}, status), { category: StatusCategory$1.PNNetworkIssuesCategory })); |
5062 | 5106 | }
|
5063 |
| - else if (status.category === StatusCategory$1.PNBadRequestCategory || |
5064 |
| - status.category == StatusCategory$1.PNMalformedResponseCategory) { |
5065 |
| - const category = this.isOnline ? StatusCategory$1.PNDisconnectedUnexpectedlyCategory : status.category; |
5066 |
| - this.isOnline = false; |
5067 |
| - this.disconnect(); |
5068 |
| - this.listenerManager.announceStatus(Object.assign(Object.assign({}, status), { category })); |
| 5107 | + else if (status.category === StatusCategory$1.PNBadRequestCategory) { |
| 5108 | + this.stopHeartbeatTimer(); |
| 5109 | + this.listenerManager.announceStatus(status); |
5069 | 5110 | }
|
5070 | 5111 | else
|
5071 | 5112 | this.listenerManager.announceStatus(status);
|
|
13319 | 13360 | */
|
13320 | 13361 | makeUnsubscribe(parameters, callback) {
|
13321 | 13362 | {
|
13322 |
| - this.sendRequest(new PresenceLeaveRequest(Object.assign(Object.assign({}, parameters), { keySet: this._configuration.keySet })), callback); |
| 13363 | + // Filtering out presence channels and groups. |
| 13364 | + let { channels, channelGroups } = parameters; |
| 13365 | + if (channelGroups) |
| 13366 | + channelGroups = channelGroups.filter((channelGroup) => !channelGroup.endsWith('-pnpres')); |
| 13367 | + if (channels) |
| 13368 | + channels = channels.filter((channel) => !channel.endsWith('-pnpres')); |
| 13369 | + // Complete immediately request only for presence channels. |
| 13370 | + if ((channelGroups !== null && channelGroups !== void 0 ? channelGroups : []).length === 0 && (channels !== null && channels !== void 0 ? channels : []).length === 0) { |
| 13371 | + return callback({ |
| 13372 | + error: false, |
| 13373 | + operation: RequestOperation$1.PNUnsubscribeOperation, |
| 13374 | + category: StatusCategory$1.PNAcknowledgmentCategory, |
| 13375 | + statusCode: 200, |
| 13376 | + }); |
| 13377 | + } |
| 13378 | + this.sendRequest(new PresenceLeaveRequest({ channels, channelGroups, keySet: this._configuration.keySet }), callback); |
13323 | 13379 | }
|
13324 | 13380 | }
|
13325 | 13381 | /**
|
|
13670 | 13726 | heartbeat(parameters, callback) {
|
13671 | 13727 | return __awaiter(this, void 0, void 0, function* () {
|
13672 | 13728 | {
|
13673 |
| - const request = new HeartbeatRequest(Object.assign(Object.assign({}, parameters), { keySet: this._configuration.keySet })); |
| 13729 | + // Filtering out presence channels and groups. |
| 13730 | + let { channels, channelGroups } = parameters; |
| 13731 | + if (channelGroups) |
| 13732 | + channelGroups = channelGroups.filter((channelGroup) => !channelGroup.endsWith('-pnpres')); |
| 13733 | + if (channels) |
| 13734 | + channels = channels.filter((channel) => !channel.endsWith('-pnpres')); |
| 13735 | + // Complete immediately request only for presence channels. |
| 13736 | + if ((channelGroups !== null && channelGroups !== void 0 ? channelGroups : []).length === 0 && (channels !== null && channels !== void 0 ? channels : []).length === 0) { |
| 13737 | + const responseStatus = { |
| 13738 | + error: false, |
| 13739 | + operation: RequestOperation$1.PNHeartbeatOperation, |
| 13740 | + category: StatusCategory$1.PNAcknowledgmentCategory, |
| 13741 | + statusCode: 200, |
| 13742 | + }; |
| 13743 | + if (callback) |
| 13744 | + return callback(responseStatus, {}); |
| 13745 | + return Promise.resolve(responseStatus); |
| 13746 | + } |
| 13747 | + const request = new HeartbeatRequest(Object.assign(Object.assign({}, parameters), { channels, |
| 13748 | + channelGroups, keySet: this._configuration.keySet })); |
13674 | 13749 | if (callback)
|
13675 | 13750 | return this.sendRequest(request, callback);
|
13676 | 13751 | return this.sendRequest(request);
|
|
0 commit comments