|
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