"use strict";
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        if (typeof b !== "function" && b !== null)
            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __exportStar = (this && this.__exportStar) || function(m, exports) {
    for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (g && (g = 0, op[0] && (_ = 0)), _) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Mqtt5Client = exports.Mqtt5WebsocketUrlFactoryType = void 0;
/**
 * Browser specific MQTT5 client implementation
 *
 * @packageDocumentation
 * @module mqtt5
 * @mergeTarget
 *
 */
var event_1 = require("../common/event");
var mqtt = __importStar(require("mqtt")); /* The mqtt-js external dependency */
var mqtt5 = __importStar(require("../common/mqtt5"));
var mqtt5_1 = require("../common/mqtt5");
var mqtt5_packet = __importStar(require("../common/mqtt5_packet"));
var error_1 = require("./error");
var WebsocketUtils = __importStar(require("./ws"));
var mqtt_utils = __importStar(require("./mqtt5_utils"));
var mqtt_shared = __importStar(require("../common/mqtt_shared"));
__exportStar(require("../common/mqtt5"), exports);
__exportStar(require("../common/mqtt5_packet"), exports);
/**
 * Type of url to construct when establishing an MQTT5 connection over websockets
 */
var Mqtt5WebsocketUrlFactoryType;
(function (Mqtt5WebsocketUrlFactoryType) {
    /**
     * Websocket connection over plain-text with no additional handshake transformation
     */
    Mqtt5WebsocketUrlFactoryType[Mqtt5WebsocketUrlFactoryType["Ws"] = 1] = "Ws";
    /**
     * Websocket connection over TLS with no additional handshake transformation
     */
    Mqtt5WebsocketUrlFactoryType[Mqtt5WebsocketUrlFactoryType["Wss"] = 2] = "Wss";
    /**
     * Websocket connection over TLS with a handshake signed by the Aws Sigv4 signing process
     */
    Mqtt5WebsocketUrlFactoryType[Mqtt5WebsocketUrlFactoryType["Sigv4"] = 3] = "Sigv4";
    /**
     * Websocket connection whose url is formed by a user-supplied callback function
     */
    Mqtt5WebsocketUrlFactoryType[Mqtt5WebsocketUrlFactoryType["Custom"] = 4] = "Custom";
})(Mqtt5WebsocketUrlFactoryType = exports.Mqtt5WebsocketUrlFactoryType || (exports.Mqtt5WebsocketUrlFactoryType = {}));
;
;
;
/**
 * @internal
 *
 * Mqtt-js only supports reconnect on a fixed delay.
 *
 * This helper class allows for variable time-delay rescheduling of reconnect attempts by implementing the
 * reconnect delay options supported by the native client.  Variable-delay reconnect actually happens by configuring
 * the mqtt-js client to have a much longer reconnect delay than our configured maximum and then letting this class
 * "interrupt" that long reconnect delay with the real, shorter wait-then-connect each time.
 */
var ReconnectionScheduler = /** @class */ (function () {
    function ReconnectionScheduler(browserClient, clientConfig) {
        this.browserClient = browserClient;
        this.clientConfig = clientConfig;
        this.connectionFailureCount = 0;
        this.lastReconnectDelay = 0;
        this.resetConnectionFailureCountTask = undefined;
        this.reconnectionTask = undefined;
        this.lastReconnectDelay = undefined;
    }
    /**
     * Invoked by the client when a successful connection is established.  Schedules the task that will reset the
     * delay if a configurable amount of time elapses with a good connection.
     */
    ReconnectionScheduler.prototype.onSuccessfulConnection = function () {
        var _this = this;
        var _a;
        this.clearTasks();
        this.resetConnectionFailureCountTask = setTimeout(function () {
            _this.connectionFailureCount = 0;
            _this.lastReconnectDelay = undefined;
        }, (_a = this.clientConfig.minConnectedTimeToResetReconnectDelayMs) !== null && _a !== void 0 ? _a : mqtt_utils.DEFAULT_MIN_CONNECTED_TIME_TO_RESET_RECONNECT_DELAY_MS);
    };
    /**
     * Invoked by the client after a disconnection or connection failure occurs.  Schedules the next reconnect
     * task.
     */
    ReconnectionScheduler.prototype.onConnectionFailureOrDisconnection = function () {
        var _this = this;
        this.clearTasks();
        var nextDelay = this.calculateNextReconnectDelay();
        this.lastReconnectDelay = nextDelay;
        this.connectionFailureCount += 1;
        this.reconnectionTask = setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
            var wsOptions, sigv4Options;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        wsOptions = this.clientConfig.websocketOptions;
                        if (!(wsOptions && wsOptions.urlFactoryOptions.urlFactory == Mqtt5WebsocketUrlFactoryType.Sigv4)) return [3 /*break*/, 2];
                        sigv4Options = wsOptions.urlFactoryOptions;
                        if (!sigv4Options.credentialsProvider) return [3 /*break*/, 2];
                        return [4 /*yield*/, sigv4Options.credentialsProvider.refreshCredentials()];
                    case 1:
                        _a.sent();
                        _a.label = 2;
                    case 2:
                        this.browserClient.reconnect();
                        return [2 /*return*/];
                }
            });
        }); }, nextDelay);
    };
    /**
     * Resets any reconnect/clear-delay tasks.
     */
    ReconnectionScheduler.prototype.clearTasks = function () {
        if (this.reconnectionTask) {
            clearTimeout(this.reconnectionTask);
        }
        if (this.resetConnectionFailureCountTask) {
            clearTimeout(this.resetConnectionFailureCountTask);
        }
    };
    ReconnectionScheduler.prototype.randomInRange = function (min, max) {
        return min + (max - min) * Math.random();
    };
    /**
     * Computes the next reconnect delay based on the Jitter/Retry configuration.
     * Implements jitter calculations in https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
     * @private
     */
    ReconnectionScheduler.prototype.calculateNextReconnectDelay = function () {
        var _a;
        var jitterType = (_a = this.clientConfig.retryJitterMode) !== null && _a !== void 0 ? _a : mqtt5.RetryJitterType.Default;
        var _b = __read(mqtt_utils.getOrderedReconnectDelayBounds(this.clientConfig.minReconnectDelayMs, this.clientConfig.maxReconnectDelayMs), 2), minDelay = _b[0], maxDelay = _b[1];
        var clampedFailureCount = Math.min(52, this.connectionFailureCount);
        var delay = 0;
        if (jitterType == mqtt5.RetryJitterType.None) {
            delay = minDelay * Math.pow(2, clampedFailureCount);
        }
        else if (jitterType == mqtt5.RetryJitterType.Decorrelated && this.lastReconnectDelay) {
            delay = this.randomInRange(minDelay, 3 * this.lastReconnectDelay);
        }
        else {
            delay = this.randomInRange(minDelay, Math.min(maxDelay, minDelay * Math.pow(2, clampedFailureCount)));
        }
        delay = Math.min(maxDelay, delay);
        this.lastReconnectDelay = delay;
        return delay;
    };
    return ReconnectionScheduler;
}());
/**
 * Elements of a simple state machine that allows us to adapt the mqtt-js control model to our mqtt5 client
 * control model (start/stop).
 *
 * @internal
 */
var Mqtt5ClientState;
(function (Mqtt5ClientState) {
    Mqtt5ClientState[Mqtt5ClientState["Stopped"] = 0] = "Stopped";
    Mqtt5ClientState[Mqtt5ClientState["Running"] = 1] = "Running";
    Mqtt5ClientState[Mqtt5ClientState["Stopping"] = 2] = "Stopping";
    Mqtt5ClientState[Mqtt5ClientState["Restarting"] = 3] = "Restarting";
})(Mqtt5ClientState || (Mqtt5ClientState = {}));
/**
 * Elements of a simple state machine that allows us to adapt the mqtt-js event set to our mqtt5 client's
 * lifecycle event set.
 *
 * @internal
 */
var Mqtt5ClientLifecycleEventState;
(function (Mqtt5ClientLifecycleEventState) {
    Mqtt5ClientLifecycleEventState[Mqtt5ClientLifecycleEventState["None"] = 0] = "None";
    Mqtt5ClientLifecycleEventState[Mqtt5ClientLifecycleEventState["Connecting"] = 1] = "Connecting";
    Mqtt5ClientLifecycleEventState[Mqtt5ClientLifecycleEventState["Connected"] = 2] = "Connected";
    Mqtt5ClientLifecycleEventState[Mqtt5ClientLifecycleEventState["Disconnected"] = 3] = "Disconnected";
})(Mqtt5ClientLifecycleEventState || (Mqtt5ClientLifecycleEventState = {}));
/**
 * Browser specific MQTT5 client implementation
 */
var Mqtt5Client = /** @class */ (function (_super) {
    __extends(Mqtt5Client, _super);
    /**
     * Client constructor
     *
     * @param config The configuration for this client
     */
    function Mqtt5Client(config) {
        var _this = _super.call(this) || this;
        _this.config = config;
        _this.on_message = function (topic, payload, packet) {
            var crtPublish = mqtt_utils.transform_mqtt_js_publish_to_crt_publish(packet);
            var messageReceivedEvent = {
                message: crtPublish
            };
            setTimeout(function () {
                _this.emit(Mqtt5Client.MESSAGE_RECEIVED, messageReceivedEvent);
            }, 0);
        };
        _this.mqttJsConfig = mqtt_utils.create_mqtt_js_client_config_from_crt_client_config(_this.config);
        _this.state = Mqtt5ClientState.Stopped;
        _this.lifecycleEventState = Mqtt5ClientLifecycleEventState.None;
        _this.topicAliasBindings = new Map();
        return _this;
    }
    /**
     * Triggers cleanup of native resources associated with the MQTT5 client.  On the browser, the implementation is
     * an empty function.
     */
    Mqtt5Client.prototype.close = function () { };
    /**
     * Notifies the MQTT5 client that you want it to maintain connectivity to the configured endpoint.
     * The client will attempt to stay connected using the properties of the reconnect-related parameters
     * in the mqtt5 client configuration.
     *
     * This is an asynchronous operation.
     */
    Mqtt5Client.prototype.start = function () {
        var _this = this;
        if (this.state == Mqtt5ClientState.Stopped) {
            this.lifecycleEventState = Mqtt5ClientLifecycleEventState.Connecting;
            this.lastDisconnect = undefined;
            /* pause event emission until everything is fully-initialized */
            this.cork();
            this.emit('attemptingConnect');
            var create_websocket_stream = function (client) { return WebsocketUtils.create_mqtt5_websocket_stream(_this.config); };
            this.browserClient = new mqtt.MqttClient(create_websocket_stream, this.mqttJsConfig);
            // hook up events
            this.browserClient.on('end', function () { _this._on_stopped_internal(); });
            this.browserClient.on('reconnect', function () { _this.on_attempting_connect(); });
            this.browserClient.on('connect', function (connack) { _this.on_connection_success(connack); });
            this.browserClient.on('message', function (topic, payload, packet) { _this.on_message(topic, payload, packet); });
            this.browserClient.on('error', function (error) { _this.on_browser_client_error(error); });
            this.browserClient.on('close', function () { _this.on_browser_close(); });
            this.browserClient.on('disconnect', function (packet) { _this.on_browser_disconnect_packet(packet); });
            this.reconnectionScheduler = new ReconnectionScheduler(this.browserClient, this.config);
            this.state = Mqtt5ClientState.Running;
            /* unpause event emission */
            this.uncork();
        }
        else if (this.state == Mqtt5ClientState.Stopping) {
            this.state = Mqtt5ClientState.Restarting;
        }
    };
    /**
     * Notifies the MQTT5 client that you want it to end connectivity to the configured endpoint, disconnecting any
     * existing connection and halting reconnection attempts.
     *
     * This is an asynchronous operation.  Once the process completes, no further events will be emitted until the client
     * has {@link start} invoked.  Invoking {@link start start()} after a {@link stop stop()} will always result in
     * a new MQTT session.
     *
     * @param disconnectPacket (optional) properties of a DISCONNECT packet to send as part of the shutdown process
     */
    Mqtt5Client.prototype.stop = function (disconnectPacket) {
        var _a, _b;
        if (this.state == Mqtt5ClientState.Running) {
            if (disconnectPacket) {
                (_a = this.browserClient) === null || _a === void 0 ? void 0 : _a.end(true, mqtt_utils.transform_crt_disconnect_to_mqtt_js_disconnect(disconnectPacket));
            }
            else {
                (_b = this.browserClient) === null || _b === void 0 ? void 0 : _b.end(true);
            }
            this.state = Mqtt5ClientState.Stopping;
        }
        else if (this.state == Mqtt5ClientState.Restarting) {
            this.state = Mqtt5ClientState.Stopping;
        }
    };
    /**
     * Subscribe to one or more topic filters by queuing a SUBSCRIBE packet to be sent to the server.
     *
     * @param packet SUBSCRIBE packet to send to the server
     * @returns a promise that will be rejected with an error or resolved with the SUBACK response
     */
    Mqtt5Client.prototype.subscribe = function (packet) {
        return __awaiter(this, void 0, void 0, function () {
            var _this = this;
            return __generator(this, function (_a) {
                return [2 /*return*/, new Promise(function (resolve, reject) {
                        try {
                            if (!_this.browserClient) {
                                reject(new Error("Client is stopped and cannot subscribe"));
                                return;
                            }
                            if (!packet) {
                                reject(new Error("Invalid subscribe packet"));
                                return;
                            }
                            var subMap = mqtt_utils.transform_crt_subscribe_to_mqtt_js_subscription_map(packet);
                            var subOptions = mqtt_utils.transform_crt_subscribe_to_mqtt_js_subscribe_options(packet);
                            // @ts-ignore
                            _this.browserClient.subscribe(subMap, subOptions, function (error, grants) {
                                if (error) {
                                    reject(error);
                                    return;
                                }
                                var suback = mqtt_utils.transform_mqtt_js_subscription_grants_to_crt_suback(grants);
                                resolve(suback);
                            });
                        }
                        catch (err) {
                            reject(err);
                        }
                    })];
            });
        });
    };
    /**
     * Unsubscribe from one or more topic filters by queuing an UNSUBSCRIBE packet to be sent to the server.
     *
     * @param packet UNSUBSCRIBE packet to send to the server
     * @returns a promise that will be rejected with an error or resolved with the UNSUBACK response
     */
    Mqtt5Client.prototype.unsubscribe = function (packet) {
        return __awaiter(this, void 0, void 0, function () {
            var _this = this;
            return __generator(this, function (_a) {
                return [2 /*return*/, new Promise(function (resolve, reject) {
                        try {
                            if (!_this.browserClient) {
                                reject(new Error("Client is stopped and cannot unsubscribe"));
                                return;
                            }
                            if (!packet) {
                                reject(new Error("Invalid unsubscribe packet"));
                                return;
                            }
                            var topicFilters_1 = packet.topicFilters;
                            var unsubOptions = mqtt_utils.transform_crt_unsubscribe_to_mqtt_js_unsubscribe_options(packet);
                            _this.browserClient.unsubscribe(topicFilters_1, unsubOptions, function (error, packet) {
                                if (error) {
                                    reject(error);
                                    return;
                                }
                                /*
                                 * sigh, mqtt-js doesn't emit the unsuback packet, we have to make something up that won't reflect
                                 * reality.
                                 */
                                if (!packet || packet.cmd !== 'unsuback') {
                                    /* this is a complete lie */
                                    var unsuback = {
                                        type: mqtt5_packet.PacketType.Unsuback,
                                        reasonCodes: topicFilters_1.map(function (filter, index, array) {
                                            return mqtt5_packet.UnsubackReasonCode.Success;
                                        })
                                    };
                                    resolve(unsuback);
                                }
                                else {
                                    var unsuback = mqtt_utils.transform_mqtt_js_unsuback_to_crt_unsuback(packet);
                                    resolve(unsuback);
                                }
                            });
                        }
                        catch (err) {
                            reject(err);
                        }
                    })];
            });
        });
    };
    Mqtt5Client.prototype.reset_topic_aliases = function () {
        this.topicAliasBindings.clear();
    };
    Mqtt5Client.prototype.bind_topic_alias = function (alias, topic) {
        this.topicAliasBindings.set(alias, topic);
    };
    Mqtt5Client.prototype.is_topic_alias_bound = function (alias, topic) {
        if (!topic) {
            return false;
        }
        return this.topicAliasBindings.get(alias) === topic;
    };
    /**
     * Send a message to subscribing clients by queuing a PUBLISH packet to be sent to the server.
     *
     * @param packet PUBLISH packet to send to the server
     * @returns a promise that will be rejected with an error or resolved with the PUBACK response (QoS 1), or
     * undefined (QoS 0)
     */
    Mqtt5Client.prototype.publish = function (packet) {
        return __awaiter(this, void 0, void 0, function () {
            var _this = this;
            return __generator(this, function (_a) {
                return [2 /*return*/, new Promise(function (resolve, reject) {
                        var _a, _b;
                        try {
                            if (!_this.browserClient) {
                                reject(new Error("Client is stopped and cannot publish"));
                                return;
                            }
                            if (!packet) {
                                reject(new Error("Invalid publish packet"));
                                return;
                            }
                            /*
                             * Out topic aliasing contract and mqtt-js's don't quite match, so we do some fixup here.
                             *
                             * In our manual mode, the contract is that you must *always* submit both the topic and the alias.
                             *
                             * In Mqtt-js's manual mode, the alias will only be used if it's been previously bound and you don't
                             * submit an alias in the publish (this is not reasonable behavior, but it's not under out control).
                             * So when we're in manual aliasing mode, we track all the current bindings and strip out the alias
                             * when there's a match, signaling to mqtt-js that the alias binding should be used.
                             */
                            if (((_b = (_a = _this.config.topicAliasingOptions) === null || _a === void 0 ? void 0 : _a.outboundBehavior) !== null && _b !== void 0 ? _b : mqtt5_1.OutboundTopicAliasBehaviorType.Default) == mqtt5_1.OutboundTopicAliasBehaviorType.Manual) {
                                if (packet.topicAlias && _this.lifecycleEventState == Mqtt5ClientLifecycleEventState.Connected) {
                                    if (_this.is_topic_alias_bound(packet.topicAlias, packet.topicName)) {
                                        delete (packet.topicAlias);
                                    }
                                    _this.bind_topic_alias(packet.topicAlias, packet.topicName);
                                }
                            }
                            else {
                                delete (packet.topicAlias);
                            }
                            var publishOptions = mqtt_utils.transform_crt_publish_to_mqtt_js_publish_options(packet);
                            var qos_1 = packet.qos;
                            var payload = mqtt_shared.normalize_payload(packet.payload);
                            _this.browserClient.publish(packet.topicName, payload, publishOptions, function (error, completionPacket) {
                                if (error) {
                                    reject(error);
                                    return;
                                }
                                switch (qos_1) {
                                    case mqtt5_packet.QoS.AtMostOnce:
                                        resolve(undefined);
                                        break;
                                    case mqtt5_packet.QoS.AtLeastOnce:
                                        if (!completionPacket) {
                                            reject(new Error("Invalid puback packet from mqtt-js"));
                                            return;
                                        }
                                        /*
                                         * sadly, mqtt-js returns the original publish packet when the puback is a success, so we have
                                         * to create a fake puback instead.  This means we won't reflect any reason string or
                                         * user properties that might have been present in the real puback.
                                         */
                                        if (completionPacket.cmd !== "puback") {
                                            resolve({
                                                type: mqtt5_packet.PacketType.Puback,
                                                reasonCode: mqtt5_packet.PubackReasonCode.Success
                                            });
                                        }
                                        var puback = mqtt_utils.transform_mqtt_js_puback_to_crt_puback(completionPacket);
                                        resolve(puback);
                                        break;
                                    default:
                                        /* Technically, mqtt-js supports QoS 2 but we don't yet model it in the CRT types */
                                        reject(new Error("Unsupported QoS value"));
                                        break;
                                }
                            });
                        }
                        catch (err) {
                            reject(err);
                        }
                    })];
            });
        });
    };
    /**
     * Queries whether the client is currently connected
     *
     * @returns whether the client is currently connected
     */
    Mqtt5Client.prototype.isConnected = function () {
        return this.lifecycleEventState == Mqtt5ClientLifecycleEventState.Connected;
    };
    Mqtt5Client.prototype.on = function (event, listener) {
        _super.prototype.on.call(this, event, listener);
        return this;
    };
    Mqtt5Client.prototype.on_browser_disconnect_packet = function (packet) {
        this.lastDisconnect = mqtt_utils.transform_mqtt_js_disconnect_to_crt_disconnect(packet);
    };
    Mqtt5Client.prototype.on_browser_close = function () {
        var _this = this;
        var _a, _b, _c, _d;
        var lastDisconnect = this.lastDisconnect;
        var lastError = this.lastError;
        if (this.lifecycleEventState == Mqtt5ClientLifecycleEventState.Connected) {
            this.lifecycleEventState = Mqtt5ClientLifecycleEventState.Disconnected;
            (_a = this.reconnectionScheduler) === null || _a === void 0 ? void 0 : _a.onConnectionFailureOrDisconnection();
            var disconnectionEvent_1 = {
                error: new error_1.CrtError((_b = lastError === null || lastError === void 0 ? void 0 : lastError.toString()) !== null && _b !== void 0 ? _b : "disconnected")
            };
            if (lastDisconnect !== undefined) {
                disconnectionEvent_1.disconnect = lastDisconnect;
            }
            setTimeout(function () {
                _this.emit(Mqtt5Client.DISCONNECTION, disconnectionEvent_1);
            }, 0);
        }
        else if (this.lifecycleEventState == Mqtt5ClientLifecycleEventState.Connecting) {
            this.lifecycleEventState = Mqtt5ClientLifecycleEventState.Disconnected;
            (_c = this.reconnectionScheduler) === null || _c === void 0 ? void 0 : _c.onConnectionFailureOrDisconnection();
            var connectionFailureEvent_1 = {
                error: new error_1.CrtError((_d = lastError === null || lastError === void 0 ? void 0 : lastError.toString()) !== null && _d !== void 0 ? _d : "connectionFailure")
            };
            setTimeout(function () {
                _this.emit(Mqtt5Client.CONNECTION_FAILURE, connectionFailureEvent_1);
            }, 0);
        }
        this.lastDisconnect = undefined;
        this.lastError = undefined;
    };
    Mqtt5Client.prototype.on_browser_client_error = function (error) {
        var _this = this;
        this.lastError = error;
        setTimeout(function () {
            _this.emit(Mqtt5Client.INFO, new error_1.CrtError(error));
        }, 0);
    };
    Mqtt5Client.prototype.on_attempting_connect = function () {
        var _this = this;
        this.lifecycleEventState = Mqtt5ClientLifecycleEventState.Connecting;
        var attemptingConnectEvent = {};
        setTimeout(function () {
            _this.emit(Mqtt5Client.ATTEMPTING_CONNECT, attemptingConnectEvent);
        }, 0);
    };
    Mqtt5Client.prototype.on_connection_success = function (connack) {
        var _this = this;
        var _a;
        this.lifecycleEventState = Mqtt5ClientLifecycleEventState.Connected;
        this.reset_topic_aliases();
        (_a = this.reconnectionScheduler) === null || _a === void 0 ? void 0 : _a.onSuccessfulConnection();
        var crt_connack = mqtt_utils.transform_mqtt_js_connack_to_crt_connack(connack);
        var settings = mqtt_utils.create_negotiated_settings(this.config, crt_connack);
        var connectionSuccessEvent = {
            connack: crt_connack,
            settings: settings
        };
        setTimeout(function () {
            _this.emit(Mqtt5Client.CONNECTION_SUCCESS, connectionSuccessEvent);
        }, 0);
    };
    Mqtt5Client.prototype._on_stopped_internal = function () {
        var _a;
        (_a = this.reconnectionScheduler) === null || _a === void 0 ? void 0 : _a.clearTasks();
        this.reconnectionScheduler = undefined;
        this.browserClient = undefined;
        this.lifecycleEventState = Mqtt5ClientLifecycleEventState.None;
        this.lastDisconnect = undefined;
        this.lastError = undefined;
        if (this.state == Mqtt5ClientState.Restarting) {
            this.state = Mqtt5ClientState.Stopped;
            this.start();
        }
        else if (this.state != Mqtt5ClientState.Stopped) {
            this.state = Mqtt5ClientState.Stopped;
            this.emit(Mqtt5Client.STOPPED);
        }
    };
    /**
     * Event emitted when the client encounters a disruptive error condition.  Not currently used.
     *
     * Listener type: {@link ErrorEventListener}
     *
     * @event
     */
    Mqtt5Client.ERROR = 'error';
    /**
     * Event emitted when the client encounters a transient error event that will not disrupt promises based on
     * lifecycle events.  Currently, mqtt-js client error events are relayed to this event.
     *
     * Listener type: {@link ErrorEventListener}
     *
     * @event
     * @group Browser-only
     */
    Mqtt5Client.INFO = 'info';
    /**
     * Event emitted when an MQTT PUBLISH packet is received by the client.
     *
     * Listener type: {@link MessageReceivedEventListener}
     *
     * @event
     */
    Mqtt5Client.MESSAGE_RECEIVED = 'messageReceived';
    /**
     * Event emitted when the client begins a connection attempt.
     *
     * Listener type: {@link AttemptingConnectEventListener}
     *
     * @event
     */
    Mqtt5Client.ATTEMPTING_CONNECT = 'attemptingConnect';
    /**
     * Event emitted when the client successfully establishes an MQTT connection.  Only emitted after
     * an {@link ATTEMPTING_CONNECT attemptingConnect} event.
     *
     * Listener type: {@link ConnectionSuccessEventListener}
     *
     * @event
     */
    Mqtt5Client.CONNECTION_SUCCESS = 'connectionSuccess';
    /**
     * Event emitted when the client fails to establish an MQTT connection.  Only emitted after
     * an {@link ATTEMPTING_CONNECT attemptingConnect} event.
     *
     * Listener type: {@link ConnectionFailureEventListener}
     *
     * @event
     */
    Mqtt5Client.CONNECTION_FAILURE = 'connectionFailure';
    /**
     * Event emitted when the client's current connection is closed for any reason.  Only emitted after
     * a {@link CONNECTION_SUCCESS connectionSuccess} event.
     *
     * Listener type: {@link DisconnectionEventListener}
     *
     * @event
     */
    Mqtt5Client.DISCONNECTION = 'disconnection';
    /**
     * Event emitted when the client finishes shutdown as a result of the user invoking {@link stop}.
     *
     * Listener type: {@link StoppedEventListener}
     *
     * @event
     */
    Mqtt5Client.STOPPED = 'stopped';
    return Mqtt5Client;
}(event_1.BufferedEventEmitter));
exports.Mqtt5Client = Mqtt5Client;
//# sourceMappingURL=mqtt5.js.map