'use strict';

var ky = require('ky');
var compat = require('es-toolkit/compat');

function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }

var ky__default = /*#__PURE__*/_interopDefault(ky);

var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });

// src/lib/utils.ts
function getFieldMask(obj) {
  let fromGrid = "";
  const fromRoot = Object.keys(obj).filter((key) => key !== "gridProperties").join(",");
  if (obj.gridProperties) {
    fromGrid = Object.keys(obj.gridProperties).map((key) => `gridProperties.${key}`).join(",");
    if (fromGrid.length && fromRoot.length) {
      fromGrid = `${fromGrid},`;
    }
  }
  return fromGrid + fromRoot;
}
__name(getFieldMask, "getFieldMask");
function columnToLetter(column) {
  let temp;
  let letter = "";
  let col = column;
  while (col > 0) {
    temp = (col - 1) % 26;
    letter = String.fromCharCode(temp + 65) + letter;
    col = (col - temp - 1) / 26;
  }
  return letter;
}
__name(columnToLetter, "columnToLetter");
function letterToColumn(letter) {
  let column = 0;
  const { length } = letter;
  for (let i = 0; i < length; i++) {
    column += (letter.charCodeAt(i) - 64) * 26 ** (length - i - 1);
  }
  return column;
}
__name(letterToColumn, "letterToColumn");
function checkForDuplicateHeaders(headers) {
  const checkForDupes = compat.groupBy(headers);
  compat.each(checkForDupes, (grouped, header) => {
    if (!header) return;
    if (grouped.length > 1) {
      throw new Error(`Duplicate header detected: "${header}". Please make sure all non-empty headers are unique`);
    }
  });
}
__name(checkForDuplicateHeaders, "checkForDuplicateHeaders");

// src/lib/GoogleSpreadsheetRow.ts
var GoogleSpreadsheetRow = class {
  constructor(_worksheet, _rowNumber, _rawData) {
    this._worksheet = _worksheet;
    this._rowNumber = _rowNumber;
    this._rawData = _rawData;
  }
  static {
    __name(this, "GoogleSpreadsheetRow");
  }
  _deleted = false;
  get deleted() {
    return this._deleted;
  }
  /** row number (matches A1 notation, ie first row is 1) */
  get rowNumber() {
    return this._rowNumber;
  }
  /**
   * @internal
   * Used internally to update row numbers after deleting rows.
   * Should not be called directly.
  */
  _updateRowNumber(newRowNumber) {
    this._rowNumber = newRowNumber;
  }
  get a1Range() {
    return [
      this._worksheet.a1SheetName,
      "!",
      `A${this._rowNumber}`,
      ":",
      `${columnToLetter(this._worksheet.headerValues.length)}${this._rowNumber}`
    ].join("");
  }
  /** get row's value of specific cell (by header key) */
  get(key) {
    const index = this._worksheet.headerValues.indexOf(key);
    return this._rawData[index];
  }
  /** set row's value of specific cell (by header key) */
  set(key, val) {
    const index = this._worksheet.headerValues.indexOf(key);
    this._rawData[index] = val;
  }
  /** set multiple values in the row at once from an object */
  assign(obj) {
    for (const key in obj) this.set(key, obj[key]);
  }
  /** return raw object of row data */
  toObject() {
    const o = {};
    for (let i = 0; i < this._worksheet.headerValues.length; i++) {
      const key = this._worksheet.headerValues[i];
      if (!key) continue;
      o[key] = this._rawData[i];
    }
    return o;
  }
  /** save row values */
  async save(options) {
    if (this._deleted) throw new Error("This row has been deleted - call getRows again before making updates.");
    const response = await this._worksheet._spreadsheet.sheetsApi.put(`values/${encodeURIComponent(this.a1Range)}`, {
      searchParams: {
        valueInputOption: options?.raw ? "RAW" : "USER_ENTERED",
        includeValuesInResponse: true
      },
      json: {
        range: this.a1Range,
        majorDimension: "ROWS",
        values: [this._rawData]
      }
    });
    const data = await response.json();
    this._rawData = data.updatedData.values[0];
  }
  /** delete this row */
  async delete() {
    if (this._deleted) throw new Error("This row has been deleted - call getRows again before making updates.");
    const result = await this._worksheet._makeSingleUpdateRequest("deleteRange", {
      range: {
        sheetId: this._worksheet.sheetId,
        startRowIndex: this._rowNumber - 1,
        // this format is zero indexed, because of course...
        endRowIndex: this._rowNumber
      },
      shiftDimension: "ROWS"
    });
    this._deleted = true;
    this._worksheet._shiftRowCache(this.rowNumber);
    return result;
  }
  /**
   * @internal
   * Used internally to clear row data after calling sheet.clearRows
   * Should not be called directly.
  */
  _clearRowData() {
    for (let i = 0; i < this._rawData.length; i++) {
      this._rawData[i] = "";
    }
  }
};

// src/lib/GoogleSpreadsheetCellErrorValue.ts
var GoogleSpreadsheetCellErrorValue = class {
  static {
    __name(this, "GoogleSpreadsheetCellErrorValue");
  }
  /**
   * type of the error
   * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/other#ErrorType
   * */
  type;
  /** A message with more information about the error (in the spreadsheet's locale) */
  message;
  constructor(rawError) {
    this.type = rawError.type;
    this.message = rawError.message;
  }
};

// src/lib/GoogleSpreadsheetCell.ts
var GoogleSpreadsheetCell = class {
  constructor(_sheet, _rowIndex, _columnIndex, rawCellData) {
    this._sheet = _sheet;
    this._rowIndex = _rowIndex;
    this._columnIndex = _columnIndex;
    this._updateRawData(rawCellData);
    this._rawData = rawCellData;
  }
  static {
    __name(this, "GoogleSpreadsheetCell");
  }
  _rawData;
  _draftData = {};
  _error;
  // TODO: figure out how to deal with empty rawData
  // newData can be undefined/null if the cell is totally empty and unformatted
  /**
   * update cell using raw CellData coming back from sheets API
   * @internal
   */
  _updateRawData(newData) {
    this._rawData = newData;
    this._draftData = {};
    if (this._rawData?.effectiveValue && "errorValue" in this._rawData.effectiveValue) {
      this._error = new GoogleSpreadsheetCellErrorValue(this._rawData.effectiveValue.errorValue);
    } else {
      this._error = void 0;
    }
  }
  // CELL LOCATION/ADDRESS /////////////////////////////////////////////////////////////////////////
  get rowIndex() {
    return this._rowIndex;
  }
  get columnIndex() {
    return this._columnIndex;
  }
  get a1Column() {
    return columnToLetter(this._columnIndex + 1);
  }
  get a1Row() {
    return this._rowIndex + 1;
  }
  // a1 row numbers start at 1 instead of 0
  get a1Address() {
    return `${this.a1Column}${this.a1Row}`;
  }
  // CELL CONTENTS - VALUE/FORMULA/NOTES ///////////////////////////////////////////////////////////
  get value() {
    if (this._draftData.value !== void 0) throw new Error("Value has been changed");
    if (this._error) return this._error;
    if (!this._rawData?.effectiveValue) return null;
    return compat.values(this._rawData.effectiveValue)[0];
  }
  set value(newValue) {
    if (newValue instanceof GoogleSpreadsheetCellErrorValue) {
      throw new Error("You can't manually set a value to an error");
    }
    if (compat.isBoolean(newValue)) {
      this._draftData.valueType = "boolValue";
    } else if (compat.isString(newValue)) {
      if (newValue.substring(0, 1) === "=") this._draftData.valueType = "formulaValue";
      else this._draftData.valueType = "stringValue";
    } else if (compat.isFinite(newValue)) {
      this._draftData.valueType = "numberValue";
    } else if (compat.isNil(newValue)) {
      this._draftData.valueType = "stringValue";
      newValue = "";
    } else {
      throw new Error("Set value to boolean, string, or number");
    }
    this._draftData.value = newValue;
  }
  get valueType() {
    if (this._error) return "errorValue";
    if (!this._rawData?.effectiveValue) return null;
    return compat.keys(this._rawData.effectiveValue)[0];
  }
  /** The formatted value of the cell - this is the value as it's shown to the user */
  get formattedValue() {
    return this._rawData?.formattedValue || null;
  }
  get formula() {
    return compat.get(this._rawData, "userEnteredValue.formulaValue", null);
  }
  set formula(newValue) {
    if (!newValue) throw new Error("To clear a formula, set `cell.value = null`");
    if (newValue.substring(0, 1) !== "=") throw new Error('formula must begin with "="');
    this.value = newValue;
  }
  /**
   * @deprecated use `cell.errorValue` instead
   */
  get formulaError() {
    return this._error;
  }
  /**
   * error contained in the cell, which can happen with a bad formula (maybe some other weird cases?)
   */
  get errorValue() {
    return this._error;
  }
  get numberValue() {
    if (this.valueType !== "numberValue") return void 0;
    return this.value;
  }
  set numberValue(val) {
    this.value = val;
  }
  get boolValue() {
    if (this.valueType !== "boolValue") return void 0;
    return this.value;
  }
  set boolValue(val) {
    this.value = val;
  }
  get stringValue() {
    if (this.valueType !== "stringValue") return void 0;
    return this.value;
  }
  set stringValue(val) {
    if (val?.startsWith("=")) {
      throw new Error("Use cell.formula to set formula values");
    }
    this.value = val;
  }
  /**
   * Hyperlink contained within the cell.
   *
   * To modify, do not set directly. Instead set cell.formula, for example `cell.formula = \'=HYPERLINK("http://google.com", "Google")\'`
   */
  get hyperlink() {
    if (this._draftData.value) throw new Error("Save cell to be able to read hyperlink");
    return this._rawData?.hyperlink;
  }
  /** a note attached to the cell */
  get note() {
    return this._draftData.note !== void 0 ? this._draftData.note : this._rawData?.note || "";
  }
  set note(newVal) {
    if (newVal === null || newVal === void 0 || newVal === false) newVal = "";
    if (!compat.isString(newVal)) throw new Error("Note must be a string");
    if (newVal === this._rawData?.note) delete this._draftData.note;
    else this._draftData.note = newVal;
  }
  // CELL FORMATTING ///////////////////////////////////////////////////////////////////////////////
  get userEnteredFormat() {
    return Object.freeze(this._rawData?.userEnteredFormat);
  }
  get effectiveFormat() {
    return Object.freeze(this._rawData?.effectiveFormat);
  }
  _getFormatParam(param) {
    if (compat.get(this._draftData, `userEnteredFormat.${param}`)) {
      throw new Error("User format is unsaved - save the cell to be able to read it again");
    }
    return Object.freeze(this._rawData.userEnteredFormat[param]);
  }
  _setFormatParam(param, newVal) {
    if (compat.isEqual(newVal, compat.get(this._rawData, `userEnteredFormat.${param}`))) {
      compat.unset(this._draftData, `userEnteredFormat.${param}`);
    } else {
      compat.set(this._draftData, `userEnteredFormat.${param}`, newVal);
      this._draftData.clearFormat = false;
    }
  }
  // format getters
  get numberFormat() {
    return this._getFormatParam("numberFormat");
  }
  get backgroundColor() {
    return this._getFormatParam("backgroundColor");
  }
  get backgroundColorStyle() {
    return this._getFormatParam("backgroundColorStyle");
  }
  get borders() {
    return this._getFormatParam("borders");
  }
  get padding() {
    return this._getFormatParam("padding");
  }
  get horizontalAlignment() {
    return this._getFormatParam("horizontalAlignment");
  }
  get verticalAlignment() {
    return this._getFormatParam("verticalAlignment");
  }
  get wrapStrategy() {
    return this._getFormatParam("wrapStrategy");
  }
  get textDirection() {
    return this._getFormatParam("textDirection");
  }
  get textFormat() {
    return this._getFormatParam("textFormat");
  }
  get hyperlinkDisplayType() {
    return this._getFormatParam("hyperlinkDisplayType");
  }
  get textRotation() {
    return this._getFormatParam("textRotation");
  }
  // format setters
  set numberFormat(newVal) {
    this._setFormatParam("numberFormat", newVal);
  }
  set backgroundColor(newVal) {
    this._setFormatParam("backgroundColor", newVal);
  }
  set backgroundColorStyle(newVal) {
    this._setFormatParam("backgroundColorStyle", newVal);
  }
  set borders(newVal) {
    this._setFormatParam("borders", newVal);
  }
  set padding(newVal) {
    this._setFormatParam("padding", newVal);
  }
  set horizontalAlignment(newVal) {
    this._setFormatParam("horizontalAlignment", newVal);
  }
  set verticalAlignment(newVal) {
    this._setFormatParam("verticalAlignment", newVal);
  }
  set wrapStrategy(newVal) {
    this._setFormatParam("wrapStrategy", newVal);
  }
  set textDirection(newVal) {
    this._setFormatParam("textDirection", newVal);
  }
  set textFormat(newVal) {
    this._setFormatParam("textFormat", newVal);
  }
  set hyperlinkDisplayType(newVal) {
    this._setFormatParam("hyperlinkDisplayType", newVal);
  }
  set textRotation(newVal) {
    this._setFormatParam("textRotation", newVal);
  }
  clearAllFormatting() {
    this._draftData.clearFormat = true;
    delete this._draftData.userEnteredFormat;
  }
  // SAVING + UTILS ////////////////////////////////////////////////////////////////////////////////
  // returns true if there are any updates that have not been saved yet
  get _isDirty() {
    if (this._draftData.note !== void 0) return true;
    if (compat.keys(this._draftData.userEnteredFormat).length) return true;
    if (this._draftData.clearFormat) return true;
    if (this._draftData.value !== void 0) return true;
    return false;
  }
  discardUnsavedChanges() {
    this._draftData = {};
  }
  /**
   * saves updates for single cell
   * usually it's better to make changes and call sheet.saveUpdatedCells
   * */
  async save() {
    await this._sheet.saveCells([this]);
  }
  /**
   * used by worksheet when saving cells
   * returns an individual batchUpdate request to update the cell
   * @internal
   */
  _getUpdateRequest() {
    const isValueUpdated = this._draftData.value !== void 0;
    const isNoteUpdated = this._draftData.note !== void 0;
    const isFormatUpdated = !!compat.keys(this._draftData.userEnteredFormat || {}).length;
    const isFormatCleared = this._draftData.clearFormat;
    if (!compat.some([isValueUpdated, isNoteUpdated, isFormatUpdated, isFormatCleared])) {
      return null;
    }
    const format = {
      // have to pass the whole object or it will clear existing properties
      ...this._rawData?.userEnteredFormat,
      ...this._draftData.userEnteredFormat
    };
    if (compat.get(this._draftData, "userEnteredFormat.backgroundColor")) {
      delete format.backgroundColorStyle;
    }
    return {
      updateCells: {
        rows: [{
          values: [{
            ...isValueUpdated && {
              userEnteredValue: { [this._draftData.valueType]: this._draftData.value }
            },
            ...isNoteUpdated && {
              note: this._draftData.note
            },
            ...isFormatUpdated && {
              userEnteredFormat: format
            },
            ...isFormatCleared && {
              userEnteredFormat: {}
            }
          }]
        }],
        // turns into a string of which fields to update ex "note,userEnteredFormat"
        fields: compat.keys(compat.pickBy({
          userEnteredValue: isValueUpdated,
          note: isNoteUpdated,
          userEnteredFormat: isFormatUpdated || isFormatCleared
        })).join(","),
        start: {
          sheetId: this._sheet.sheetId,
          rowIndex: this.rowIndex,
          columnIndex: this.columnIndex
        }
      }
    };
  }
};

// src/lib/GoogleSpreadsheetWorksheet.ts
var GoogleSpreadsheetWorksheet = class {
  constructor(_spreadsheet, rawProperties, rawCellData) {
    this._spreadsheet = _spreadsheet;
    this._headerRowIndex = 1;
    this._rawProperties = rawProperties;
    this._cells = [];
    this._rowMetadata = [];
    this._columnMetadata = [];
    if (rawCellData) this._fillCellData(rawCellData);
  }
  static {
    __name(this, "GoogleSpreadsheetWorksheet");
  }
  // assume "header row" (for row-based calls) is in first row, can be adjusted later
  _headerRowIndex = 1;
  _rawProperties = null;
  _cells = [];
  _rowMetadata = [];
  _columnMetadata = [];
  _headerValues;
  get headerValues() {
    if (!this._headerValues) {
      throw new Error("Header values are not yet loaded");
    }
    return this._headerValues;
  }
  // INTERNAL UTILITY FUNCTIONS ////////////////////////////////////////////////////////////////////
  updateRawData(properties, rawCellData) {
    this._rawProperties = properties;
    this._fillCellData(rawCellData);
  }
  async _makeSingleUpdateRequest(requestType, requestParams) {
    return this._spreadsheet._makeSingleUpdateRequest(requestType, {
      ...requestParams
    });
  }
  _ensureInfoLoaded() {
    if (!this._rawProperties) {
      throw new Error("You must call `doc.loadInfo()` again before accessing this property");
    }
  }
  /** clear local cache of sheet data/properties */
  resetLocalCache(dataOnly) {
    if (!dataOnly) this._rawProperties = null;
    this._headerValues = void 0;
    this._headerRowIndex = 1;
    this._cells = [];
  }
  _fillCellData(dataRanges) {
    compat.each(dataRanges, (range) => {
      const startRow = range.startRow || 0;
      const startColumn = range.startColumn || 0;
      const numRows = range.rowMetadata.length;
      const numColumns = range.columnMetadata.length;
      for (let i = 0; i < numRows; i++) {
        const actualRow = startRow + i;
        for (let j = 0; j < numColumns; j++) {
          const actualColumn = startColumn + j;
          if (!this._cells[actualRow]) this._cells[actualRow] = [];
          const cellData = compat.get(range, `rowData[${i}].values[${j}]`);
          if (this._cells[actualRow][actualColumn]) {
            this._cells[actualRow][actualColumn]._updateRawData(cellData);
          } else {
            this._cells[actualRow][actualColumn] = new GoogleSpreadsheetCell(
              this,
              actualRow,
              actualColumn,
              cellData
            );
          }
        }
      }
      for (let i = 0; i < range.rowMetadata.length; i++) {
        this._rowMetadata[startRow + i] = range.rowMetadata[i];
      }
      for (let i = 0; i < range.columnMetadata.length; i++) {
        this._columnMetadata[startColumn + i] = range.columnMetadata[i];
      }
    });
  }
  // TODO: make this handle A1 ranges as well?
  _addSheetIdToRange(range) {
    if (range.sheetId && range.sheetId !== this.sheetId) {
      throw new Error("Leave sheet ID blank or set to matching ID of this sheet");
    }
    return {
      ...range,
      sheetId: this.sheetId
    };
  }
  // PROPERTY GETTERS //////////////////////////////////////////////////////////////////////////////
  _getProp(param) {
    this._ensureInfoLoaded();
    return this._rawProperties[param];
  }
  // eslint-disable-line no-unused-vars
  _setProp(_param, _newVal) {
    throw new Error("Do not update directly - use `updateProperties()`");
  }
  get sheetId() {
    return this._getProp("sheetId");
  }
  get title() {
    return this._getProp("title");
  }
  get index() {
    return this._getProp("index");
  }
  get sheetType() {
    return this._getProp("sheetType");
  }
  get gridProperties() {
    return this._getProp("gridProperties");
  }
  get hidden() {
    return this._getProp("hidden");
  }
  get tabColor() {
    return this._getProp("tabColor");
  }
  get rightToLeft() {
    return this._getProp("rightToLeft");
  }
  get _headerRange() {
    return `A${this._headerRowIndex}:${this.lastColumnLetter}${this._headerRowIndex}`;
  }
  set sheetId(newVal) {
    this._setProp("sheetId", newVal);
  }
  set title(newVal) {
    this._setProp("title", newVal);
  }
  set index(newVal) {
    this._setProp("index", newVal);
  }
  set sheetType(newVal) {
    this._setProp("sheetType", newVal);
  }
  set gridProperties(newVal) {
    this._setProp("gridProperties", newVal);
  }
  set hidden(newVal) {
    this._setProp("hidden", newVal);
  }
  set tabColor(newVal) {
    this._setProp("tabColor", newVal);
  }
  set rightToLeft(newVal) {
    this._setProp("rightToLeft", newVal);
  }
  get rowCount() {
    this._ensureInfoLoaded();
    return this.gridProperties.rowCount;
  }
  get columnCount() {
    this._ensureInfoLoaded();
    return this.gridProperties.columnCount;
  }
  get a1SheetName() {
    return `'${this.title.replace(/'/g, "''")}'`;
  }
  get encodedA1SheetName() {
    return encodeURIComponent(this.a1SheetName);
  }
  get lastColumnLetter() {
    return this.columnCount ? columnToLetter(this.columnCount) : "";
  }
  // CELLS-BASED INTERACTIONS //////////////////////////////////////////////////////////////////////
  get cellStats() {
    let allCells = compat.flatten(this._cells);
    allCells = compat.compact(allCells);
    return {
      nonEmpty: compat.filter(allCells, (c) => c.value).length,
      loaded: allCells.length,
      total: this.rowCount * this.columnCount
    };
  }
  getCellByA1(a1Address) {
    const split = a1Address.match(/([A-Z]+)([0-9]+)/);
    if (!split) throw new Error(`Cell address "${a1Address}" not valid`);
    const columnIndex = letterToColumn(split[1]);
    const rowIndex = parseInt(split[2]);
    return this.getCell(rowIndex - 1, columnIndex - 1);
  }
  getCell(rowIndex, columnIndex) {
    if (rowIndex < 0 || columnIndex < 0) throw new Error("Min coordinate is 0, 0");
    if (rowIndex >= this.rowCount || columnIndex >= this.columnCount) {
      throw new Error(`Out of bounds, sheet is ${this.rowCount} by ${this.columnCount}`);
    }
    if (!compat.get(this._cells, `[${rowIndex}][${columnIndex}]`)) {
      throw new Error("This cell has not been loaded yet");
    }
    return this._cells[rowIndex][columnIndex];
  }
  async loadCells(sheetFilters) {
    if (!sheetFilters) return this._spreadsheet.loadCells(this.a1SheetName);
    const filtersArray = compat.isArray(sheetFilters) ? sheetFilters : [sheetFilters];
    const filtersArrayWithSheetId = compat.map(filtersArray, (filter2) => {
      if (compat.isString(filter2)) {
        if (filter2.startsWith(this.a1SheetName)) return filter2;
        return `${this.a1SheetName}!${filter2}`;
      }
      if (compat.isObject(filter2)) {
        const filterAny = filter2;
        if (filterAny.sheetId && filterAny.sheetId !== this.sheetId) {
          throw new Error("Leave sheet ID blank or set to matching ID of this sheet");
        }
        return { sheetId: this.sheetId, ...filter2 };
      }
      throw new Error("Each filter must be a A1 range string or gridrange object");
    });
    return this._spreadsheet.loadCells(filtersArrayWithSheetId);
  }
  async saveUpdatedCells() {
    const cellsToSave = compat.filter(compat.flatten(this._cells), { _isDirty: true });
    if (cellsToSave.length) {
      await this.saveCells(cellsToSave);
    }
  }
  async saveCells(cellsToUpdate) {
    const requests = compat.map(cellsToUpdate, (cell) => cell._getUpdateRequest());
    const responseRanges = compat.map(cellsToUpdate, (c) => `${this.a1SheetName}!${c.a1Address}`);
    if (!compat.compact(requests).length) {
      throw new Error("At least one cell must have something to update");
    }
    await this._spreadsheet._makeBatchUpdateRequest(requests, responseRanges);
  }
  // SAVING THIS FOR FUTURE USE
  // puts the cells that need updating into batches
  // async updateCellsByBatches() {
  //   // saving this code, but it's problematic because each group must have the same update fields
  //   const cellsByRow = _.groupBy(cellsToUpdate, 'rowIndex');
  //   const groupsToSave = [];
  //   _.each(cellsByRow, (cells, rowIndex) => {
  //     let cellGroup = [];
  //     _.each(cells, (c) => {
  //       if (!cellGroup.length) {
  //         cellGroup.push(c);
  //       } else if (
  //         cellGroup[cellGroup.length - 1].columnIndex ===
  //         c.columnIndex - 1
  //       ) {
  //         cellGroup.push(c);
  //       } else {
  //         groupsToSave.push(cellGroup);
  //         cellGroup = [];
  //       }
  //     });
  //     groupsToSave.push(cellGroup);
  //   });
  //   const requests = _.map(groupsToSave, (cellGroup) => ({
  //     updateCells: {
  //       rows: [
  //         {
  //           values: _.map(cellGroup, (cell) => ({
  //             ...cell._draftData.value && {
  //               userEnteredValue: { [cell._draftData.valueType]: cell._draftData.value },
  //             },
  //             ...cell._draftData.note !== undefined && {
  //               note: cell._draftData.note ,
  //             },
  //             ...cell._draftData.userEnteredFormat && {
  //               userEnteredValue: cell._draftData.userEnteredFormat,
  //             },
  //           })),
  //         },
  //       ],
  //       fields: 'userEnteredValue,note,userEnteredFormat',
  //       start: {
  //         sheetId: this.sheetId,
  //         rowIndex: cellGroup[0].rowIndex,
  //         columnIndex: cellGroup[0].columnIndex,
  //       },
  //     },
  //   }));
  //   const responseRanges = _.map(groupsToSave, (cellGroup) => {
  //     let a1Range = cellGroup[0].a1Address;
  //     if (cellGroup.length > 1)
  //       a1Range += `:${cellGroup[cellGroup.length - 1].a1Address}`;
  //     return `${cellGroup[0]._sheet.a1SheetName}!${a1Range}`;
  //   });
  // }
  // ROW BASED FUNCTIONS ///////////////////////////////////////////////////////////////////////////
  async _ensureHeaderRowLoaded() {
    if (!this._headerValues) {
      await this.loadHeaderRow();
    }
  }
  async loadHeaderRow(headerRowIndex) {
    if (headerRowIndex !== void 0) this._headerRowIndex = headerRowIndex;
    const rows = await this.getCellsInRange(this._headerRange);
    this._processHeaderRow(rows);
  }
  _processHeaderRow(rows) {
    if (!rows) {
      throw new Error("No values in the header row - fill the first row with header values before trying to interact with rows");
    }
    this._headerValues = compat.map(rows[0], (header) => header?.trim());
    if (!compat.compact(this.headerValues).length) {
      throw new Error("All your header cells are blank - fill the first row with header values before trying to interact with rows");
    }
    checkForDuplicateHeaders(this.headerValues);
  }
  async setHeaderRow(headerValues, headerRowIndex) {
    if (!headerValues) return;
    if (headerValues.length > this.columnCount) {
      throw new Error(`Sheet is not large enough to fit ${headerValues.length} columns. Resize the sheet first.`);
    }
    const trimmedHeaderValues = compat.map(headerValues, (h) => h?.trim());
    checkForDuplicateHeaders(trimmedHeaderValues);
    if (!compat.compact(trimmedHeaderValues).length) {
      throw new Error("All your header cells are blank -");
    }
    if (headerRowIndex) this._headerRowIndex = headerRowIndex;
    const response = await this._spreadsheet.sheetsApi.put(
      `values/${this.encodedA1SheetName}!${this._headerRowIndex}:${this._headerRowIndex}`,
      {
        searchParams: {
          valueInputOption: "USER_ENTERED",
          // other option is RAW
          includeValuesInResponse: true
        },
        json: {
          range: `${this.a1SheetName}!${this._headerRowIndex}:${this._headerRowIndex}`,
          majorDimension: "ROWS",
          values: [[
            ...trimmedHeaderValues,
            // pad the rest of the row with empty values to clear them all out
            ...compat.times(this.columnCount - trimmedHeaderValues.length, () => "")
          ]]
        }
      }
    );
    const data = await response.json();
    this._headerValues = data.updatedData.values[0];
  }
  // TODO: look at these types
  async addRows(rows, options = {}) {
    if (this.title.includes(":")) {
      throw new Error('Please remove the ":" from your sheet title. There is a bug with the google API which breaks appending rows if any colons are in the sheet title.');
    }
    if (!compat.isArray(rows)) throw new Error("You must pass in an array of row values to append");
    await this._ensureHeaderRowLoaded();
    const rowsAsArrays = [];
    compat.each(rows, (row) => {
      let rowAsArray;
      if (compat.isArray(row)) {
        rowAsArray = row;
      } else if (compat.isObject(row)) {
        rowAsArray = [];
        for (let i = 0; i < this.headerValues.length; i++) {
          const propName = this.headerValues[i];
          rowAsArray[i] = row[propName];
        }
      } else {
        throw new Error("Each row must be an object or an array");
      }
      rowsAsArrays.push(rowAsArray);
    });
    const response = await this._spreadsheet.sheetsApi.post(
      `values/${this.encodedA1SheetName}!A${this._headerRowIndex}:append`,
      {
        searchParams: {
          valueInputOption: options.raw ? "RAW" : "USER_ENTERED",
          insertDataOption: options.insert ? "INSERT_ROWS" : "OVERWRITE",
          includeValuesInResponse: true
        },
        json: {
          values: rowsAsArrays
        }
      }
    );
    const data = await response.json();
    const { updatedRange } = data.updates;
    let rowNumber = updatedRange.match(/![A-Z]+([0-9]+):?/)[1];
    rowNumber = parseInt(rowNumber);
    this._ensureInfoLoaded();
    if (options.insert) {
      this._rawProperties.gridProperties.rowCount += rows.length;
    } else if (rowNumber + rows.length > this.rowCount) {
      this._rawProperties.gridProperties.rowCount = rowNumber + rows.length - 1;
    }
    return compat.map(data.updates.updatedData.values, (rowValues) => {
      const row = new GoogleSpreadsheetRow(this, rowNumber++, rowValues);
      return row;
    });
  }
  /** add a single row - see addRows for more info */
  async addRow(rowValues, options) {
    const rows = await this.addRows([rowValues], options);
    return rows[0];
  }
  _rowCache = [];
  async getRows(options) {
    const offset = options?.offset || 0;
    const limit = options?.limit || this.rowCount - 1;
    const firstRow = 1 + this._headerRowIndex + offset;
    const lastRow = firstRow + limit - 1;
    let rawRows;
    if (this._headerValues) {
      const lastColumn = columnToLetter(this.headerValues.length);
      rawRows = await this.getCellsInRange(
        `A${firstRow}:${lastColumn}${lastRow}`
      );
    } else {
      const result = await this.batchGetCellsInRange([
        this._headerRange,
        `A${firstRow}:${this.lastColumnLetter}${lastRow}`
      ]);
      this._processHeaderRow(result[0]);
      rawRows = result[1];
    }
    if (!rawRows) return [];
    const rows = [];
    let rowNum = firstRow;
    for (let i = 0; i < rawRows.length; i++) {
      const row = new GoogleSpreadsheetRow(this, rowNum++, rawRows[i]);
      this._rowCache[row.rowNumber] = row;
      rows.push(row);
    }
    return rows;
  }
  /**
   * @internal
   * Used internally to update row numbers after deleting rows.
   * Should not be called directly.
   * */
  _shiftRowCache(deletedRowNumber) {
    delete this._rowCache[deletedRowNumber];
    this._rowCache.forEach((row) => {
      if (row.rowNumber > deletedRowNumber) {
        row._updateRowNumber(row.rowNumber - 1);
      }
    });
  }
  async clearRows(options) {
    const startRowIndex = options?.start || this._headerRowIndex + 1;
    const endRowIndex = options?.end || this.rowCount;
    await this._spreadsheet.sheetsApi.post(`values/${this.encodedA1SheetName}!${startRowIndex}:${endRowIndex}:clear`);
    this._rowCache.forEach((row) => {
      if (row.rowNumber >= startRowIndex && row.rowNumber <= endRowIndex) row._clearRowData();
    });
  }
  // BASIC PROPS ///////////////////////////////////////////////////////////////////////////////////
  /** @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateSheetPropertiesRequest */
  async updateProperties(properties) {
    return this._makeSingleUpdateRequest("updateSheetProperties", {
      properties: {
        sheetId: this.sheetId,
        ...properties
      },
      fields: getFieldMask(properties)
    });
  }
  /**
   * passes through the call to updateProperties to update only the gridProperties object
   */
  async updateGridProperties(gridProperties) {
    return this.updateProperties({ gridProperties });
  }
  /** resize, internally just calls updateGridProperties */
  async resize(gridProperties) {
    return this.updateGridProperties(gridProperties);
  }
  /**
   *
   * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#updatedimensionpropertiesrequest
   */
  async updateDimensionProperties(columnsOrRows, properties, bounds) {
    return this._makeSingleUpdateRequest("updateDimensionProperties", {
      range: {
        sheetId: this.sheetId,
        dimension: columnsOrRows,
        ...bounds
      },
      properties,
      fields: getFieldMask(properties)
    });
  }
  // OTHER /////////////////////////////////////////////////////////////////////////////////////////
  // this uses the "values" getter and does not give all the info about the cell contents
  // it is used internally when loading header cells
  async getCellsInRange(a1Range, options) {
    const response = await this._spreadsheet.sheetsApi.get(`values/${this.encodedA1SheetName}!${a1Range}`, {
      searchParams: options
    });
    const data = await response.json();
    return data.values;
  }
  async batchGetCellsInRange(a1Ranges, options) {
    const ranges = a1Ranges.map((r) => `ranges=${this.encodedA1SheetName}!${r}`).join("&");
    const response = await this._spreadsheet.sheetsApi.get(`values:batchGet?${ranges}`, {
      searchParams: options
    });
    const data = await response.json();
    return data.valueRanges.map((r) => r.values);
  }
  async updateNamedRange() {
  }
  async addNamedRange() {
  }
  async deleteNamedRange() {
  }
  async repeatCell() {
  }
  async autoFill() {
  }
  async cutPaste() {
  }
  async copyPaste() {
  }
  // TODO: check types on these ranges
  /**
   * Merges all cells in the range
   * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#MergeCellsRequest
   */
  async mergeCells(range, mergeType = "MERGE_ALL") {
    await this._makeSingleUpdateRequest("mergeCells", {
      mergeType,
      range: this._addSheetIdToRange(range)
    });
  }
  /**
   * Unmerges cells in the given range
   * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UnmergeCellsRequest
   */
  async unmergeCells(range) {
    await this._makeSingleUpdateRequest("unmergeCells", {
      range: this._addSheetIdToRange(range)
    });
  }
  async updateBorders() {
  }
  async addFilterView() {
  }
  async appendCells() {
  }
  async clearBasicFilter() {
  }
  async deleteDimension() {
  }
  async deleteEmbeddedObject() {
  }
  async deleteFilterView() {
  }
  async duplicateFilterView() {
  }
  /**
   * Duplicate worksheet within the document
   * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DuplicateSheetRequest
   */
  async duplicate(options) {
    const response = await this._makeSingleUpdateRequest("duplicateSheet", {
      sourceSheetId: this.sheetId,
      ...options?.index !== void 0 && { insertSheetIndex: options.index },
      ...options?.id && { newSheetId: options.id },
      ...options?.title && { newSheetName: options.title }
    });
    const newSheetId = response.properties.sheetId;
    return this._spreadsheet.sheetsById[newSheetId];
  }
  async findReplace() {
  }
  /**
   * Inserts rows or columns at a particular index
   * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#InsertDimensionRequest
   */
  async insertDimension(columnsOrRows, rangeIndexes, inheritFromBefore) {
    if (!columnsOrRows) throw new Error("You need to specify a dimension. i.e. COLUMNS|ROWS");
    if (!compat.isObject(rangeIndexes)) throw new Error("`range` must be an object containing `startIndex` and `endIndex`");
    if (!compat.isInteger(rangeIndexes.startIndex) || rangeIndexes.startIndex < 0) throw new Error("range.startIndex must be an integer >=0");
    if (!compat.isInteger(rangeIndexes.endIndex) || rangeIndexes.endIndex < 0) throw new Error("range.endIndex must be an integer >=0");
    if (rangeIndexes.endIndex <= rangeIndexes.startIndex) throw new Error("range.endIndex must be greater than range.startIndex");
    if (inheritFromBefore === void 0) {
      inheritFromBefore = rangeIndexes.startIndex > 0;
    }
    if (inheritFromBefore && rangeIndexes.startIndex === 0) {
      throw new Error("Cannot set inheritFromBefore to true if inserting in first row/column");
    }
    return this._makeSingleUpdateRequest("insertDimension", {
      range: {
        sheetId: this.sheetId,
        dimension: columnsOrRows,
        startIndex: rangeIndexes.startIndex,
        endIndex: rangeIndexes.endIndex
      },
      inheritFromBefore
    });
  }
  async insertRange() {
  }
  async moveDimension() {
  }
  async updateEmbeddedObjectPosition() {
  }
  async pasteData() {
  }
  async textToColumns() {
  }
  async updateFilterView() {
  }
  async deleteRange() {
  }
  async appendDimension() {
  }
  async addConditionalFormatRule() {
  }
  async updateConditionalFormatRule() {
  }
  async deleteConditionalFormatRule() {
  }
  async sortRange() {
  }
  /**
   * Sets (or unsets) a data validation rule to every cell in the range
   * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#SetDataValidationRequest
   */
  async setDataValidation(range, rule) {
    return this._makeSingleUpdateRequest("setDataValidation", {
      range: {
        sheetId: this.sheetId,
        ...range
      },
      ...rule && { rule }
    });
  }
  async setBasicFilter() {
  }
  async addProtectedRange() {
  }
  async updateProtectedRange() {
  }
  async deleteProtectedRange() {
  }
  async autoResizeDimensions() {
  }
  async addChart() {
  }
  async updateChartSpec() {
  }
  async updateBanding() {
  }
  async addBanding() {
  }
  async deleteBanding() {
  }
  async createDeveloperMetadata() {
  }
  async updateDeveloperMetadata() {
  }
  async deleteDeveloperMetadata() {
  }
  async randomizeRange() {
  }
  async addDimensionGroup() {
  }
  async deleteDimensionGroup() {
  }
  async updateDimensionGroup() {
  }
  async trimWhitespace() {
  }
  async deleteDuplicates() {
  }
  async addSlicer() {
  }
  async updateSlicerSpec() {
  }
  /** delete this worksheet */
  async delete() {
    return this._spreadsheet.deleteSheet(this.sheetId);
  }
  /**
   * copies this worksheet into another document/spreadsheet
   * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.sheets/copyTo
   * */
  async copyToSpreadsheet(destinationSpreadsheetId) {
    const req = this._spreadsheet.sheetsApi.post(`sheets/${this.sheetId}:copyTo`, {
      json: {
        destinationSpreadsheetId
      }
    });
    const data = await req.json();
    return data;
  }
  /** clear data in the sheet - either the entire sheet or a specific range */
  async clear(a1Range) {
    const range = a1Range ? `!${a1Range}` : "";
    await this._spreadsheet.sheetsApi.post(`values/${this.encodedA1SheetName}${range}:clear`);
    this.resetLocalCache(true);
  }
  async downloadAsCSV(returnStreamInsteadOfBuffer = false) {
    return this._spreadsheet._downloadAs("csv", this.sheetId, returnStreamInsteadOfBuffer);
  }
  async downloadAsTSV(returnStreamInsteadOfBuffer = false) {
    return this._spreadsheet._downloadAs("tsv", this.sheetId, returnStreamInsteadOfBuffer);
  }
  async downloadAsPDF(returnStreamInsteadOfBuffer = false) {
    return this._spreadsheet._downloadAs("pdf", this.sheetId, returnStreamInsteadOfBuffer);
  }
};

// src/lib/GoogleSpreadsheet.ts
var SHEETS_API_BASE_URL = "https://sheets.googleapis.com/v4/spreadsheets";
var DRIVE_API_BASE_URL = "https://www.googleapis.com/drive/v3/files";
var EXPORT_CONFIG = {
  html: {},
  zip: {},
  xlsx: {},
  ods: {},
  csv: { singleWorksheet: true },
  tsv: { singleWorksheet: true },
  pdf: { singleWorksheet: true }
};
function getAuthMode(auth) {
  if ("getRequestHeaders" in auth) return "google_auth" /* GOOGLE_AUTH_CLIENT */;
  if ("token" in auth && auth.token) return "raw_access_token" /* RAW_ACCESS_TOKEN */;
  if ("apiKey" in auth && auth.apiKey) return "api_key" /* API_KEY */;
  throw new Error("Invalid auth");
}
__name(getAuthMode, "getAuthMode");
async function getRequestAuthConfig(auth) {
  if ("getRequestHeaders" in auth) {
    const headers = await auth.getRequestHeaders();
    if ("entries" in headers) {
      return { headers: Object.fromEntries(headers.entries()) };
    }
    if (compat.isObject(headers)) {
      return { headers };
    }
    throw new Error("unexpected headers returned from getRequestHeaders");
  }
  if ("apiKey" in auth && auth.apiKey) {
    return { searchParams: { key: auth.apiKey } };
  }
  if ("token" in auth && auth.token) {
    return { headers: { Authorization: `Bearer ${auth.token}` } };
  }
  throw new Error("Invalid auth");
}
__name(getRequestAuthConfig, "getRequestAuthConfig");
var GoogleSpreadsheet = class _GoogleSpreadsheet {
  static {
    __name(this, "GoogleSpreadsheet");
  }
  spreadsheetId;
  auth;
  get authMode() {
    return getAuthMode(this.auth);
  }
  _rawSheets;
  _rawProperties = null;
  _spreadsheetUrl = null;
  _deleted = false;
  /**
   * Sheets API [ky](https://github.com/sindresorhus/ky?tab=readme-ov-file#kycreatedefaultoptions) instance
   * authentication is automatically attached
   * can be used if unsupported sheets calls need to be made
   * @see https://developers.google.com/sheets/api/reference/rest
   * */
  sheetsApi;
  /**
   * Drive API [ky](https://github.com/sindresorhus/ky?tab=readme-ov-file#kycreatedefaultoptions) instance
   * authentication automatically attached
   * can be used if unsupported drive calls need to be made
   * @topic permissions
   * @see https://developers.google.com/drive/api/v3/reference
   * */
  driveApi;
  /**
   * initialize new GoogleSpreadsheet
   * @category Initialization
   * */
  constructor(spreadsheetId, auth) {
    this.spreadsheetId = spreadsheetId;
    this.auth = auth;
    this._rawSheets = {};
    this._spreadsheetUrl = null;
    this.sheetsApi = ky__default.default.create({
      prefixUrl: `${SHEETS_API_BASE_URL}/${spreadsheetId}`,
      hooks: {
        beforeRequest: [(r) => this._setAuthRequestHook(r)],
        beforeError: [(e) => this._errorHook(e)]
      }
    });
    this.driveApi = ky__default.default.create({
      prefixUrl: `${DRIVE_API_BASE_URL}/${spreadsheetId}`,
      hooks: {
        beforeRequest: [(r) => this._setAuthRequestHook(r)],
        beforeError: [(e) => this._errorHook(e)]
      }
    });
  }
  // INTERNAL UTILITY FUNCTIONS ////////////////////////////////////////////////////////////////////
  /** @internal */
  async _setAuthRequestHook(req) {
    const authConfig = await getRequestAuthConfig(this.auth);
    if (authConfig.headers) {
      Object.entries(authConfig.headers).forEach(([key, val]) => {
        req.headers.set(key, String(val));
      });
    }
    if (authConfig.searchParams) {
      const url = new URL(req.url);
      Object.entries(authConfig.searchParams).forEach(([key, val]) => {
        url.searchParams.set(key, String(val));
      });
      return new Request(url, req);
    }
    return req;
  }
  /** @internal */
  async _errorHook(error) {
    const { response } = error;
    const errorDataText = await response?.text();
    let errorData;
    try {
      errorData = JSON.parse(errorDataText);
    } catch (e) {
    }
    if (errorData) {
      if (!errorData.error) return error;
      const { code, message } = errorData.error;
      error.message = `Google API error - [${code}] ${message}`;
      return error;
    }
    if (compat.get(error, "response.status") === 403) {
      if ("apiKey" in this.auth) {
        throw new Error("Sheet is private. Use authentication or make public. (see https://github.com/theoephraim/node-google-spreadsheet#a-note-on-authentication for details)");
      }
    }
    return error;
  }
  /** @internal */
  async _makeSingleUpdateRequest(requestType, requestParams) {
    const response = await this.sheetsApi.post(":batchUpdate", {
      json: {
        requests: [{ [requestType]: requestParams }],
        includeSpreadsheetInResponse: true
        // responseRanges: [string]
        // responseIncludeGridData: true
      }
    });
    const data = await response.json();
    this._updateRawProperties(data.updatedSpreadsheet.properties);
    compat.each(data.updatedSpreadsheet.sheets, (s) => this._updateOrCreateSheet(s));
    return data.replies[0][requestType];
  }
  // TODO: review these types
  // currently only used in batching cell updates
  /** @internal */
  async _makeBatchUpdateRequest(requests, responseRanges) {
    const response = await this.sheetsApi.post(":batchUpdate", {
      json: {
        requests,
        includeSpreadsheetInResponse: true,
        ...responseRanges && {
          responseIncludeGridData: true,
          ...responseRanges !== "*" && { responseRanges }
        }
      }
    });
    const data = await response.json();
    this._updateRawProperties(data.updatedSpreadsheet.properties);
    compat.each(data.updatedSpreadsheet.sheets, (s) => this._updateOrCreateSheet(s));
  }
  /** @internal */
  _ensureInfoLoaded() {
    if (!this._rawProperties) throw new Error("You must call `doc.loadInfo()` before accessing this property");
  }
  /** @internal */
  _updateRawProperties(newProperties) {
    this._rawProperties = newProperties;
  }
  /** @internal */
  _updateOrCreateSheet(sheetInfo) {
    const { properties, data } = sheetInfo;
    const { sheetId } = properties;
    if (!this._rawSheets[sheetId]) {
      this._rawSheets[sheetId] = new GoogleSpreadsheetWorksheet(this, properties, data);
    } else {
      this._rawSheets[sheetId].updateRawData(properties, data);
    }
  }
  // BASIC PROPS //////////////////////////////////////////////////////////////////////////////
  _getProp(param) {
    this._ensureInfoLoaded();
    return this._rawProperties[param];
  }
  get title() {
    return this._getProp("title");
  }
  get locale() {
    return this._getProp("locale");
  }
  get timeZone() {
    return this._getProp("timeZone");
  }
  get autoRecalc() {
    return this._getProp("autoRecalc");
  }
  get defaultFormat() {
    return this._getProp("defaultFormat");
  }
  get spreadsheetTheme() {
    return this._getProp("spreadsheetTheme");
  }
  get iterativeCalculationSettings() {
    return this._getProp("iterativeCalculationSettings");
  }
  /**
   * update spreadsheet properties
   * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets#SpreadsheetProperties
   * */
  async updateProperties(properties) {
    await this._makeSingleUpdateRequest("updateSpreadsheetProperties", {
      properties,
      fields: getFieldMask(properties)
    });
  }
  // BASIC INFO ////////////////////////////////////////////////////////////////////////////////////
  async loadInfo(includeCells = false) {
    const response = await this.sheetsApi.get("", {
      searchParams: {
        ...includeCells && { includeGridData: true }
      }
    });
    const data = await response.json();
    this._spreadsheetUrl = data.spreadsheetUrl;
    this._rawProperties = data.properties;
    data.sheets?.forEach((s) => this._updateOrCreateSheet(s));
  }
  resetLocalCache() {
    this._rawProperties = null;
    this._rawSheets = {};
  }
  // WORKSHEETS ////////////////////////////////////////////////////////////////////////////////////
  get sheetCount() {
    this._ensureInfoLoaded();
    return compat.values(this._rawSheets).length;
  }
  get sheetsById() {
    this._ensureInfoLoaded();
    return this._rawSheets;
  }
  get sheetsByIndex() {
    this._ensureInfoLoaded();
    return compat.sortBy(this._rawSheets, "index");
  }
  get sheetsByTitle() {
    this._ensureInfoLoaded();
    return compat.keyBy(this._rawSheets, "title");
  }
  /**
   * Add new worksheet to document
   * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddSheetRequest
   * */
  async addSheet(properties = {}) {
    const response = await this._makeSingleUpdateRequest("addSheet", {
      properties: compat.omit(properties, "headerValues", "headerRowIndex")
    });
    const newSheetId = response.properties.sheetId;
    const newSheet = this.sheetsById[newSheetId];
    if (properties.headerValues) {
      await newSheet.setHeaderRow(properties.headerValues, properties.headerRowIndex);
    }
    return newSheet;
  }
  /**
   * delete a worksheet
   * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DeleteSheetRequest
   * */
  async deleteSheet(sheetId) {
    await this._makeSingleUpdateRequest("deleteSheet", { sheetId });
    delete this._rawSheets[sheetId];
  }
  // NAMED RANGES //////////////////////////////////////////////////////////////////////////////////
  /**
   * create a new named range
   * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddNamedRangeRequest
   */
  async addNamedRange(name, range, namedRangeId) {
    return this._makeSingleUpdateRequest("addNamedRange", {
      name,
      namedRangeId,
      range
    });
  }
  /**
   * delete a named range
   * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DeleteNamedRangeRequest
   * */
  async deleteNamedRange(namedRangeId) {
    return this._makeSingleUpdateRequest("deleteNamedRange", { namedRangeId });
  }
  // LOADING CELLS /////////////////////////////////////////////////////////////////////////////////
  /** fetch cell data into local cache */
  async loadCells(filters) {
    const readOnlyMode = this.authMode === "api_key" /* API_KEY */;
    const filtersArray = compat.isArray(filters) ? filters : [filters];
    const dataFilters = compat.map(filtersArray, (filter2) => {
      if (compat.isString(filter2)) {
        return readOnlyMode ? filter2 : { a1Range: filter2 };
      }
      if (compat.isObject(filter2)) {
        if (readOnlyMode) {
          throw new Error("Only A1 ranges are supported when fetching cells with read-only access (using only an API key)");
        }
        return { gridRange: filter2 };
      }
      throw new Error("Each filter must be an A1 range string or a gridrange object");
    });
    let result;
    if (this.authMode === "api_key" /* API_KEY */) {
      const params = new URLSearchParams();
      params.append("includeGridData", "true");
      dataFilters.forEach((singleFilter) => {
        if (!compat.isString(singleFilter)) {
          throw new Error("Only A1 ranges are supported when fetching cells with read-only access (using only an API key)");
        }
        params.append("ranges", singleFilter);
      });
      result = await this.sheetsApi.get("", {
        searchParams: params
      });
    } else {
      result = await this.sheetsApi.post(":getByDataFilter", {
        json: {
          includeGridData: true,
          dataFilters
        }
      });
    }
    const data = await result?.json();
    compat.each(data.sheets, (sheet) => {
      this._updateOrCreateSheet(sheet);
    });
  }
  // EXPORTING /////////////////////////////////////////////////////////////
  /**
   * export/download helper, not meant to be called directly (use downloadAsX methods on spreadsheet and worksheet instead)
   * @internal
   */
  async _downloadAs(fileType, worksheetId, returnStreamInsteadOfBuffer) {
    if (!EXPORT_CONFIG[fileType]) throw new Error(`unsupported export fileType - ${fileType}`);
    if (EXPORT_CONFIG[fileType].singleWorksheet) {
      if (worksheetId === void 0) throw new Error(`Must specify worksheetId when exporting as ${fileType}`);
    } else if (worksheetId) throw new Error(`Cannot specify worksheetId when exporting as ${fileType}`);
    if (fileType === "html") fileType = "zip";
    if (!this._spreadsheetUrl) throw new Error("Cannot export sheet that is not fully loaded");
    const exportUrl = this._spreadsheetUrl.replace("edit", "export");
    const response = await this.sheetsApi.get(exportUrl, {
      prefixUrl: "",
      // unset baseUrl since we're not hitting the normal sheets API
      searchParams: {
        id: this.spreadsheetId,
        format: fileType,
        // worksheetId can be 0
        ...worksheetId !== void 0 && { gid: worksheetId }
      }
    });
    if (returnStreamInsteadOfBuffer) {
      return response.body;
    }
    return response.arrayBuffer();
  }
  async downloadAsZippedHTML(returnStreamInsteadOfBuffer) {
    return this._downloadAs("html", void 0, returnStreamInsteadOfBuffer);
  }
  /**
   * @deprecated
   * use `doc.downloadAsZippedHTML()` instead
   * */
  async downloadAsHTML(returnStreamInsteadOfBuffer) {
    return this._downloadAs("html", void 0, returnStreamInsteadOfBuffer);
  }
  async downloadAsXLSX(returnStreamInsteadOfBuffer = false) {
    return this._downloadAs("xlsx", void 0, returnStreamInsteadOfBuffer);
  }
  async downloadAsODS(returnStreamInsteadOfBuffer = false) {
    return this._downloadAs("ods", void 0, returnStreamInsteadOfBuffer);
  }
  async delete() {
    await this.driveApi.delete("");
    this._deleted = true;
  }
  // PERMISSIONS ///////////////////////////////////////////////////////////////////////////////////
  /**
   * list all permissions entries for doc
   */
  async listPermissions() {
    const listReq = await this.driveApi.get("permissions", {
      searchParams: {
        fields: "permissions(id,type,emailAddress,domain,role,displayName,photoLink,deleted)"
      }
    });
    const data = await listReq.json();
    return data.permissions;
  }
  async setPublicAccessLevel(role) {
    const permissions = await this.listPermissions();
    const existingPublicPermission = compat.find(permissions, (p) => p.type === "anyone");
    if (role === false) {
      if (!existingPublicPermission) {
        return;
      }
      await this.driveApi.delete(`permissions/${existingPublicPermission.id}`);
    } else {
      await this.driveApi.post("permissions", {
        json: {
          role: role || "viewer",
          type: "anyone"
        }
      });
    }
  }
  /** share document to email or domain */
  async share(emailAddressOrDomain, opts) {
    let emailAddress;
    let domain;
    if (emailAddressOrDomain.includes("@")) {
      emailAddress = emailAddressOrDomain;
    } else {
      domain = emailAddressOrDomain;
    }
    const shareReq = await this.driveApi.post("permissions", {
      searchParams: {
        ...opts?.emailMessage === false && { sendNotificationEmail: false },
        ...compat.isString(opts?.emailMessage) && { emailMessage: opts?.emailMessage },
        ...opts?.role === "owner" && { transferOwnership: true }
      },
      json: {
        role: opts?.role || "writer",
        ...emailAddress && {
          type: opts?.isGroup ? "group" : "user",
          emailAddress
        },
        ...domain && {
          type: "domain",
          domain
        }
      }
    });
    return shareReq.json();
  }
  //
  // CREATE NEW DOC ////////////////////////////////////////////////////////////////////////////////
  static async createNewSpreadsheetDocument(auth, properties) {
    if (getAuthMode(auth) === "api_key" /* API_KEY */) {
      throw new Error("Cannot use api key only to create a new spreadsheet - it is only usable for read-only access of public docs");
    }
    const authConfig = await getRequestAuthConfig(auth);
    const response = await ky__default.default.post(SHEETS_API_BASE_URL, {
      ...authConfig,
      // has the auth header
      json: {
        properties
      }
    });
    const data = await response.json();
    const newSpreadsheet = new _GoogleSpreadsheet(data.spreadsheetId, auth);
    newSpreadsheet._spreadsheetUrl = data.spreadsheetUrl;
    newSpreadsheet._rawProperties = data.properties;
    compat.each(data.sheets, (s) => newSpreadsheet._updateOrCreateSheet(s));
    return newSpreadsheet;
  }
};

exports.GoogleSpreadsheet = GoogleSpreadsheet;
exports.GoogleSpreadsheetCell = GoogleSpreadsheetCell;
exports.GoogleSpreadsheetCellErrorValue = GoogleSpreadsheetCellErrorValue;
exports.GoogleSpreadsheetRow = GoogleSpreadsheetRow;
exports.GoogleSpreadsheetWorksheet = GoogleSpreadsheetWorksheet;
//# sourceMappingURL=index.cjs.map
//# sourceMappingURL=index.cjs.map