parse(uri) {
    const connectionStringParser = new RegExp(
      "^\\s*" + // Optional whitespace padding at the beginning of the line
        "([^:]+)://" + // Scheme (Group 1)
        "(?:([^:@,/?=&]+)(?::([^:@,/?=&]+))?@)?" + // User (Group 2) and Password (Group 3)
        "([^@/?=&]+)" + // Host address(es) (Group 4)
        "(?:/([^:@,/?=&]+)?)?" + // Endpoint (Group 5)
        "(?:\\?([^:@,/?]+)?)?" + // Options (Group 6)
        "\\s*$", // Optional whitespace padding at the end of the line
      "gi"
    );
    const connectionStringObject = {};

    if (!uri.includes("://")) {
      throw new Error(`No scheme found in URI ${uri}`);
    }

    const tokens = connectionStringParser.exec(uri);

    if (Array.isArray(tokens)) {
      connectionStringObject.scheme = tokens[1];
      if (this.scheme && this.scheme !== connectionStringObject.scheme) {
        throw new Error(`URI must start with '${this.scheme}://'`);
      }
      connectionStringObject.username = tokens[2]
        ? decodeURIComponent(tokens[2])
        : tokens[2];
      connectionStringObject.password = tokens[3]
        ? decodeURIComponent(tokens[3])
        : tokens[3];
      connectionStringObject.hosts = this._parseAddress(tokens[4]);
      connectionStringObject.endpoint = tokens[5]
        ? decodeURIComponent(tokens[5])
        : tokens[5];
      connectionStringObject.options = tokens[6]
        ? this._parseOptions(tokens[6])
        : tokens[6];
    }
    return connectionStringObject;
  }
Where scheme and hosts will always be present. Other fields will only be present in the result if they were
present in the input.
@param {string} uri The connection string URI
@returns {ConnectionStringObject} The connection string object