async createSession_() {
    const url = createSessionUrl + '?key=' + this.apiKey_;
    const config = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(this.sessionTokenRequest_),
    };

    const response = await this.fetchSessionToken(url, config);
    if (!response.ok) {
      try {
        const body = await response.json();
        this.error_ = new Error(body.error.message);
      } catch {
        this.error_ = new Error('Error fetching session token');
      }
      this.setState('error');
      return;
    }

    /**
     * @type {SessionTokenResponse}
     */
    const sessionTokenResponse = await response.json();

    const tilePixelRatio = this.getTilePixelRatio(1);
    const tileSize = [
      sessionTokenResponse.tileWidth / tilePixelRatio,
      sessionTokenResponse.tileHeight / tilePixelRatio,
    ];

    this.tileGrid = createXYZ({
      extent: extentFromProjection(this.getProjection()),
      maxZoom: maxZoom,
      tileSize: tileSize,
    });

    const session = sessionTokenResponse.session;
    this.sessionTokenValue_ = session;
    const key = this.apiKey_;
    this.tileUrlFunction = function (tileCoord, pixelRatio, projection) {
      const z = tileCoord[0];
      const x = tileCoord[1];
      const y = tileCoord[2];
      const url = `${tileUrl}/${z}/${x}/${y}?session=${session}&key=${key}`;
      return url;
    };

    const expiry = parseInt(sessionTokenResponse.expiry, 10) * 1000;
    const timeout = Math.max(expiry - Date.now() - 60 * 1000, 1);
    this.sessionRefreshId_ = setTimeout(() => this.createSession_(), timeout);

    this.setAttributions(this.fetchAttributions_.bind(this));
    // even if the state is already ready, we want the change event
    this.setState('ready');
  }
Get or renew a session token for use with tile requests.
@private