google-spreadsheet
Version:
Google Sheets API -- simple interface to read/write data and manage sheets
1 lines • 140 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","names":["HTTPError"],"sources":["../src/lib/utils.ts","../src/lib/GoogleSpreadsheetRow.ts","../src/lib/GoogleSpreadsheetCellErrorValue.ts","../src/lib/GoogleSpreadsheetCell.ts","../src/lib/GoogleSpreadsheetWorksheet.ts","../src/lib/types/auth-types.ts","../src/lib/GoogleSpreadsheet.ts"],"sourcesContent":["import * as _ from './toolkit';\n\nexport function getFieldMask(obj: Record<string, unknown>) {\n let fromGrid = '';\n const fromRoot = Object.keys(obj).filter((key) => key !== 'gridProperties').join(',');\n\n if (obj.gridProperties) {\n fromGrid = Object.keys(obj.gridProperties).map((key) => `gridProperties.${key}`).join(',');\n if (fromGrid.length && fromRoot.length) {\n fromGrid = `${fromGrid},`;\n }\n }\n return fromGrid + fromRoot;\n}\n\nexport function columnToLetter(column: number) {\n let temp;\n let letter = '';\n let col = column;\n while (col > 0) {\n temp = (col - 1) % 26;\n letter = String.fromCharCode(temp + 65) + letter;\n col = (col - temp - 1) / 26;\n }\n return letter;\n}\n\nexport function letterToColumn(letter: string) {\n let column = 0;\n const { length } = letter;\n for (let i = 0; i < length; i++) {\n column += (letter.charCodeAt(i) - 64) * 26 ** (length - i - 1);\n }\n return column;\n}\n\nexport function checkForDuplicateHeaders(headers: string[]) {\n // check for duplicate headers\n const checkForDupes = _.groupBy(headers); // { c1: ['c1'], c2: ['c2', 'c2' ]}\n _.each(checkForDupes, (grouped, header) => {\n if (!header) return; // empty columns are skipped, so multiple is ok\n if (grouped.length > 1) {\n throw new Error(`Duplicate header detected: \"${header}\". Please make sure all non-empty headers are unique`);\n }\n });\n}\n\n","import { GoogleSpreadsheetWorksheet } from './GoogleSpreadsheetWorksheet';\nimport { columnToLetter } from './utils';\n\n\n// TODO: add type for possible row values (currently any)\n\nexport class GoogleSpreadsheetRow<T extends Record<string, any> = Record<string, any>> {\n constructor(\n /** parent GoogleSpreadsheetWorksheet instance */\n readonly _worksheet: GoogleSpreadsheetWorksheet,\n /** the A1 row (1-indexed) */\n private _rowNumber: number,\n /** raw underlying data for row */\n private _rawData: any[]\n ) {\n this._padRawData();\n }\n\n /** pad _rawData with empty strings so it always matches header length */\n private _padRawData() {\n const headerLength = this._worksheet.headerValues.length;\n while (this._rawData.length < headerLength) {\n this._rawData.push('');\n }\n }\n\n private _deleted = false;\n get deleted() { return this._deleted; }\n\n /** row number (matches A1 notation, ie first row is 1) */\n get rowNumber() { return this._rowNumber; }\n /**\n * @internal\n * Used internally to update row numbers after deleting rows.\n * Should not be called directly.\n */\n _updateRowNumber(newRowNumber: number) {\n this._rowNumber = newRowNumber;\n }\n\n /**\n * @internal\n * Used internally to mark row as deleted.\n * Should not be called directly.\n */\n _markDeleted() {\n this._deleted = true;\n }\n\n get a1Range() {\n return [\n this._worksheet.a1SheetName,\n '!',\n `A${this._rowNumber}`,\n ':',\n `${columnToLetter(this._worksheet.headerValues.length)}${this._rowNumber}`,\n ].join('');\n }\n\n /** get row's value of specific cell (by header key) */\n get(key: keyof T) {\n const index = this._worksheet.headerValues.indexOf(key as string);\n return this._rawData[index];\n }\n /** set row's value of specific cell (by header key) */\n set<K extends keyof T>(key: K, val: T[K]) {\n const index = this._worksheet.headerValues.indexOf(key as string);\n this._rawData[index] = val;\n }\n /** set multiple values in the row at once from an object */\n assign(obj: Partial<T>) {\n // eslint-disable-next-line no-restricted-syntax, guard-for-in\n for (const key in obj) this.set(key, obj[key] as any);\n }\n\n /** return raw object of row data */\n toObject() {\n const o: Partial<T> = {};\n for (let i = 0; i < this._worksheet.headerValues.length; i++) {\n const key: keyof T = this._worksheet.headerValues[i];\n if (!key) continue;\n o[key] = this._rawData[i];\n }\n return o;\n }\n\n /** save row values */\n async save(options?: { raw?: boolean }) {\n if (this._deleted) throw new Error('This row has been deleted - call getRows again before making updates.');\n\n const response = await this._worksheet._spreadsheet.sheetsApi.put(`values/${encodeURIComponent(this.a1Range)}`, {\n searchParams: {\n valueInputOption: options?.raw ? 'RAW' : 'USER_ENTERED',\n includeValuesInResponse: true,\n },\n json: {\n range: this.a1Range,\n majorDimension: 'ROWS',\n values: [this._rawData],\n },\n });\n const data = await response.json<any>();\n this._rawData = data.updatedData.values?.[0] || [];\n this._padRawData();\n }\n\n /** delete this row */\n async delete() {\n if (this._deleted) throw new Error('This row has been deleted - call getRows again before making updates.');\n\n const result = await this._worksheet._makeSingleUpdateRequest('deleteRange', {\n range: {\n sheetId: this._worksheet.sheetId,\n startRowIndex: this._rowNumber - 1, // this format is zero indexed, because of course...\n endRowIndex: this._rowNumber,\n },\n shiftDimension: 'ROWS',\n });\n this._deleted = true;\n this._worksheet._shiftRowCache(this.rowNumber);\n\n return result;\n }\n\n /**\n * @internal\n * Used internally to clear row data after calling sheet.clearRows\n * Should not be called directly.\n */\n _clearRowData() {\n for (let i = 0; i < this._rawData.length; i++) {\n this._rawData[i] = '';\n }\n }\n}\n","import { CellValueErrorType, ErrorValue } from './types/sheets-types';\n\n/**\n * Cell error\n *\n * not a js \"error\" that gets thrown, but a value that holds an error code and message for a cell\n * it's useful to use a class so we can check `instanceof`\n\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/other#ErrorType\n */\nexport class GoogleSpreadsheetCellErrorValue {\n /**\n * type of the error\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/other#ErrorType\n * */\n readonly type: CellValueErrorType;\n\n /** A message with more information about the error (in the spreadsheet's locale) */\n readonly message: string;\n\n constructor(rawError: ErrorValue) {\n this.type = rawError.type;\n this.message = rawError.message;\n }\n}\n","/* eslint-disable max-classes-per-file */\nimport * as _ from './toolkit';\n\nimport { columnToLetter } from './utils';\n\nimport { GoogleSpreadsheetWorksheet } from './GoogleSpreadsheetWorksheet';\nimport { GoogleSpreadsheetCellErrorValue } from './GoogleSpreadsheetCellErrorValue';\n\nimport {\n CellData,\n CellFormat, CellValueType, ColumnIndex, RowIndex,\n} from './types/sheets-types';\n\nexport class GoogleSpreadsheetCell {\n private _rawData?: CellData;\n private _draftData: any = {};\n private _error?: GoogleSpreadsheetCellErrorValue;\n private _deleted = false;\n\n constructor(\n readonly _sheet: GoogleSpreadsheetWorksheet,\n private _rowIndex: RowIndex,\n private _columnIndex: ColumnIndex,\n rawCellData: CellData\n ) {\n this._updateRawData(rawCellData);\n this._rawData = rawCellData; // so TS does not complain\n }\n\n get deleted() { return this._deleted; }\n\n // TODO: figure out how to deal with empty rawData\n // newData can be undefined/null if the cell is totally empty and unformatted\n /**\n * update cell using raw CellData coming back from sheets API\n * @internal\n */\n _updateRawData(newData: CellData) {\n this._rawData = newData;\n this._draftData = {};\n if (this._rawData?.effectiveValue && 'errorValue' in this._rawData.effectiveValue) {\n this._error = new GoogleSpreadsheetCellErrorValue(this._rawData.effectiveValue.errorValue);\n } else {\n this._error = undefined;\n }\n }\n\n // CELL LOCATION/ADDRESS /////////////////////////////////////////////////////////////////////////\n get rowIndex() { return this._rowIndex; }\n get columnIndex() { return this._columnIndex; }\n get a1Column() { return columnToLetter(this._columnIndex + 1); }\n get a1Row() { return this._rowIndex + 1; } // a1 row numbers start at 1 instead of 0\n get a1Address() { return `${this.a1Column}${this.a1Row}`; }\n\n /**\n * @internal\n * Used internally to update cell indices after deleting rows/columns.\n * Should not be called directly.\n */\n _updateIndices(rowIndex: RowIndex, columnIndex: ColumnIndex) {\n this._rowIndex = rowIndex;\n this._columnIndex = columnIndex;\n }\n\n /**\n * @internal\n * Used internally to mark cell as deleted.\n * Should not be called directly.\n */\n _markDeleted() {\n this._deleted = true;\n }\n\n // CELL CONTENTS - VALUE/FORMULA/NOTES ///////////////////////////////////////////////////////////\n get value(): number | boolean | string | null | GoogleSpreadsheetCellErrorValue {\n // const typeKey = _.keys(this._rawData.effectiveValue)[0];\n if (this._draftData.value !== undefined) throw new Error('Value has been changed');\n if (this._error) return this._error;\n if (!this._rawData?.effectiveValue) return null;\n return _.values(this._rawData.effectiveValue)[0];\n }\n\n\n set value(newValue: number | boolean | Date | string | null | undefined | GoogleSpreadsheetCellErrorValue) {\n if (this._deleted) throw new Error('This cell has been deleted - reload cells before making updates.');\n\n // had to include the GoogleSpreadsheetCellErrorValue in the type to make TS happy\n if (newValue instanceof GoogleSpreadsheetCellErrorValue) {\n throw new Error(\"You can't manually set a value to an error\");\n }\n\n if (_.isBoolean(newValue)) {\n this._draftData.valueType = 'boolValue';\n } else if (_.isString(newValue)) {\n if (newValue.substring(0, 1) === '=') this._draftData.valueType = 'formulaValue';\n else this._draftData.valueType = 'stringValue';\n } else if (_.isFinite(newValue)) {\n this._draftData.valueType = 'numberValue';\n } else if (_.isNil(newValue)) {\n // null or undefined\n this._draftData.valueType = 'stringValue';\n newValue = '';\n } else {\n throw new Error('Set value to boolean, string, or number');\n }\n this._draftData.value = newValue;\n }\n\n get valueType(): CellValueType | null {\n // an error only happens with a formula (as far as I know)\n if (this._error) return 'errorValue';\n if (!this._rawData?.effectiveValue) return null;\n return _.keys(this._rawData.effectiveValue)[0] as CellValueType;\n }\n\n /** The formatted value of the cell - this is the value as it's shown to the user */\n get formattedValue(): string | null { return this._rawData?.formattedValue || null; }\n\n get formula() { return _.get(this._rawData, 'userEnteredValue.formulaValue', null); }\n set formula(newValue: string | null) {\n if (!newValue) throw new Error('To clear a formula, set `cell.value = null`');\n if (newValue.substring(0, 1) !== '=') throw new Error('formula must begin with \"=\"');\n this.value = newValue; // use existing value setter\n }\n /**\n * @deprecated use `cell.errorValue` instead\n */\n get formulaError() { return this._error; }\n /**\n * error contained in the cell, which can happen with a bad formula (maybe some other weird cases?)\n */\n get errorValue() { return this._error; }\n\n get numberValue(): number | undefined {\n if (this.valueType !== 'numberValue') return undefined;\n return this.value as number;\n }\n set numberValue(val: number | undefined) {\n this.value = val;\n }\n\n get boolValue(): boolean | undefined {\n if (this.valueType !== 'boolValue') return undefined;\n return this.value as boolean;\n }\n set boolValue(val: boolean | undefined) {\n this.value = val;\n }\n\n get stringValue(): string | undefined {\n if (this.valueType !== 'stringValue') return undefined;\n return this.value as string;\n }\n set stringValue(val: string | undefined) {\n this._draftData.valueType = 'stringValue';\n this._draftData.value = val || '';\n }\n\n /**\n * Hyperlink contained within the cell.\n *\n * To modify, do not set directly. Instead set cell.formula, for example `cell.formula = \\'=HYPERLINK(\"http://google.com\", \"Google\")\\'`\n */\n get hyperlink() {\n if (this._draftData.value) throw new Error('Save cell to be able to read hyperlink');\n return this._rawData?.hyperlink;\n }\n\n /** a note attached to the cell */\n get note(): string {\n return this._draftData.note !== undefined ? this._draftData.note : this._rawData?.note || '';\n }\n set note(newVal: string | null | undefined | false) {\n if (newVal === null || newVal === undefined || newVal === false) newVal = '';\n if (!_.isString(newVal)) throw new Error('Note must be a string');\n if (newVal === this._rawData?.note) delete this._draftData.note;\n else this._draftData.note = newVal;\n }\n\n // CELL FORMATTING ///////////////////////////////////////////////////////////////////////////////\n get userEnteredFormat() { return Object.freeze(this._rawData?.userEnteredFormat); }\n get effectiveFormat() { return Object.freeze(this._rawData?.effectiveFormat); }\n\n private _getFormatParam<T extends keyof CellFormat>(param: T): Readonly<CellFormat[T]> {\n // we freeze the object so users don't change nested props accidentally\n // TODO: figure out something that would throw an error if you try to update it?\n if (_.get(this._draftData, `userEnteredFormat.${param}`)) {\n throw new Error('User format is unsaved - save the cell to be able to read it again');\n }\n // TODO: figure out how to deal with possible empty rawData\n // if (!this._rawData?.userEnteredFormat?.[param]) {\n // return undefined;\n // }\n return Object.freeze(this._rawData!.userEnteredFormat[param]);\n }\n\n private _setFormatParam<T extends keyof CellFormat>(param: T, newVal: CellFormat[T]) {\n if (_.isEqual(newVal, _.get(this._rawData, `userEnteredFormat.${param}`))) {\n _.unset(this._draftData, `userEnteredFormat.${param}`);\n } else {\n _.set(this._draftData, `userEnteredFormat.${param}`, newVal);\n this._draftData.clearFormat = false;\n }\n }\n\n // format getters\n get numberFormat() { return this._getFormatParam('numberFormat'); }\n get backgroundColor() { return this._getFormatParam('backgroundColor'); }\n get backgroundColorStyle() { return this._getFormatParam('backgroundColorStyle'); }\n get borders() { return this._getFormatParam('borders'); }\n get padding() { return this._getFormatParam('padding'); }\n get horizontalAlignment() { return this._getFormatParam('horizontalAlignment'); }\n get verticalAlignment() { return this._getFormatParam('verticalAlignment'); }\n get wrapStrategy() { return this._getFormatParam('wrapStrategy'); }\n get textDirection() { return this._getFormatParam('textDirection'); }\n get textFormat() { return this._getFormatParam('textFormat'); }\n get hyperlinkDisplayType() { return this._getFormatParam('hyperlinkDisplayType'); }\n get textRotation() { return this._getFormatParam('textRotation'); }\n\n // format setters\n set numberFormat(newVal: CellFormat['numberFormat']) { this._setFormatParam('numberFormat', newVal); }\n set backgroundColor(newVal: CellFormat['backgroundColor']) { this._setFormatParam('backgroundColor', newVal); }\n set backgroundColorStyle(newVal: CellFormat['backgroundColorStyle']) { this._setFormatParam('backgroundColorStyle', newVal); }\n set borders(newVal: CellFormat['borders']) { this._setFormatParam('borders', newVal); }\n set padding(newVal: CellFormat['padding']) { this._setFormatParam('padding', newVal); }\n set horizontalAlignment(newVal: CellFormat['horizontalAlignment']) { this._setFormatParam('horizontalAlignment', newVal); }\n set verticalAlignment(newVal: CellFormat['verticalAlignment']) { this._setFormatParam('verticalAlignment', newVal); }\n set wrapStrategy(newVal: CellFormat['wrapStrategy']) { this._setFormatParam('wrapStrategy', newVal); }\n set textDirection(newVal: CellFormat['textDirection']) { this._setFormatParam('textDirection', newVal); }\n set textFormat(newVal: CellFormat['textFormat']) { this._setFormatParam('textFormat', newVal); }\n set hyperlinkDisplayType(newVal: CellFormat['hyperlinkDisplayType']) { this._setFormatParam('hyperlinkDisplayType', newVal); }\n set textRotation(newVal: CellFormat['textRotation']) { this._setFormatParam('textRotation', newVal); }\n\n clearAllFormatting() {\n // need to track this separately since by setting/unsetting things, we may end up with\n // this._draftData.userEnteredFormat as an empty object, but not an intent to clear it\n this._draftData.clearFormat = true;\n delete this._draftData.userEnteredFormat;\n }\n\n // SAVING + UTILS ////////////////////////////////////////////////////////////////////////////////\n\n // returns true if there are any updates that have not been saved yet\n get _isDirty() {\n // have to be careful about checking undefined rather than falsy\n // in case a new value is empty string or 0 or false\n if (this._draftData.note !== undefined) return true;\n if (_.keys(this._draftData.userEnteredFormat).length) return true;\n if (this._draftData.clearFormat) return true;\n if (this._draftData.value !== undefined) return true;\n return false;\n }\n\n discardUnsavedChanges() {\n this._draftData = {};\n }\n\n /**\n * saves updates for single cell\n * usually it's better to make changes and call sheet.saveUpdatedCells\n * */\n async save() {\n await this._sheet.saveCells([this]);\n }\n\n /**\n * used by worksheet when saving cells\n * returns an individual batchUpdate request to update the cell\n * @internal\n */\n _getUpdateRequest() {\n // this logic should match the _isDirty logic above\n // but we need it broken up to build the request below\n const isValueUpdated = this._draftData.value !== undefined;\n const isNoteUpdated = this._draftData.note !== undefined;\n const isFormatUpdated = !!_.keys(this._draftData.userEnteredFormat || {}).length;\n const isFormatCleared = this._draftData.clearFormat;\n\n // if no updates, we return null, which we can filter out later before sending requests\n if (!_.some([isValueUpdated, isNoteUpdated, isFormatUpdated, isFormatCleared])) {\n return null;\n }\n\n // build up the formatting object, which has some quirks...\n const format = {\n // have to pass the whole object or it will clear existing properties\n ...this._rawData?.userEnteredFormat,\n ...this._draftData.userEnteredFormat,\n };\n // if background color already set, cell has backgroundColor and backgroundColorStyle\n // but backgroundColorStyle takes precendence so we must remove to set the color\n // see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/cells#CellFormat\n if (_.get(this._draftData, 'userEnteredFormat.backgroundColor')) {\n delete (format.backgroundColorStyle);\n }\n\n return {\n updateCells: {\n rows: [{\n values: [{\n ...isValueUpdated && {\n userEnteredValue: { [this._draftData.valueType]: this._draftData.value },\n },\n ...isNoteUpdated && {\n note: this._draftData.note,\n },\n ...isFormatUpdated && {\n userEnteredFormat: format,\n },\n ...isFormatCleared && {\n userEnteredFormat: {},\n },\n }],\n }],\n // turns into a string of which fields to update ex \"note,userEnteredFormat\"\n fields: _.keys(_.pickBy({\n userEnteredValue: isValueUpdated,\n note: isNoteUpdated,\n userEnteredFormat: isFormatUpdated || isFormatCleared,\n })).join(','),\n start: {\n sheetId: this._sheet.sheetId,\n rowIndex: this.rowIndex,\n columnIndex: this.columnIndex,\n },\n },\n };\n }\n}\n","import { type ReadableStream } from 'stream/web';\nimport * as _ from './toolkit';\n\nimport { GoogleSpreadsheetRow } from './GoogleSpreadsheetRow';\nimport { GoogleSpreadsheetCell } from './GoogleSpreadsheetCell';\n\nimport {\n getFieldMask, columnToLetter, letterToColumn, checkForDuplicateHeaders,\n} from './utils';\nimport { GoogleSpreadsheet } from './GoogleSpreadsheet';\nimport {\n A1Range, SpreadsheetId, DimensionRangeIndexes, WorksheetDimension, WorksheetId, WorksheetProperties, A1Address,\n RowIndex, ColumnIndex, DataFilterWithoutWorksheetId, DataFilter, GetValuesRequestOptions, WorksheetGridProperties,\n WorksheetDimensionProperties, CellDataRange, AddRowOptions, GridRangeWithOptionalWorksheetId,\n DataValidationRule,\n ProtectedRange, Integer, GridCoordinateWithOptionalWorksheetId, PasteType, DelimiterType, PasteOrientation,\n SortSpec, SourceAndDestination, DimensionRange,\n FilterView, ConditionalFormatRule, BandedRange, DeveloperMetadata, DataFilterObject,\n} from './types/sheets-types';\n\n\n// types of cell data accepted when using row based api\ntype RowCellData = string | number | boolean | Date;\n// raw row data can be passed in as an array or an object using header values as keys\ntype RawRowData = RowCellData[] | Record<string, RowCellData>;\n\nexport class GoogleSpreadsheetWorksheet {\n // assume \"header row\" (for row-based calls) is in first row, can be adjusted later\n private _headerRowIndex = 1;\n\n private _rawProperties: WorksheetProperties | null = null;\n private _cells: GoogleSpreadsheetCell[][] = [];\n private _rowMetadata: any[] = [];\n private _columnMetadata: any[] = [];\n private _protectedRanges: ProtectedRange[] | null = null;\n\n private _headerValues: string[] | undefined;\n get headerValues() {\n if (!this._headerValues) {\n throw new Error('Header values are not yet loaded');\n }\n return this._headerValues!;\n }\n\n constructor(\n /** parent GoogleSpreadsheet instance */\n readonly _spreadsheet: GoogleSpreadsheet,\n rawProperties: WorksheetProperties,\n rawCellData?: CellDataRange[],\n protectedRanges?: ProtectedRange[]\n ) {\n this._headerRowIndex = 1;\n\n // basic properties\n this._rawProperties = rawProperties;\n\n this._cells = []; // we will use a 2d sparse array to store cells;\n\n this._rowMetadata = []; // 1d sparse array\n this._columnMetadata = [];\n if (protectedRanges) this._protectedRanges = protectedRanges;\n\n if (rawCellData) this._fillCellData(rawCellData);\n }\n\n // INTERNAL UTILITY FUNCTIONS ////////////////////////////////////////////////////////////////////\n\n updateRawData(properties: WorksheetProperties, rawCellData: CellDataRange[], protectedRanges?: ProtectedRange[]) {\n this._rawProperties = properties;\n this._fillCellData(rawCellData);\n if (protectedRanges) this._protectedRanges = protectedRanges;\n }\n\n async _makeSingleUpdateRequest(requestType: string, requestParams: any) {\n // pass the call up to the parent\n return this._spreadsheet._makeSingleUpdateRequest(requestType, {\n ...requestParams,\n });\n }\n\n private _ensureInfoLoaded() {\n if (!this._rawProperties) {\n throw new Error('You must call `doc.loadInfo()` again before accessing this property');\n }\n }\n\n /**\n * clear local cache of sheet data/properties\n */\n resetLocalCache(\n /** set to true to clear data only, leaving sheet metadata/propeties intact */\n dataOnly?: boolean\n ) {\n if (!dataOnly) this._rawProperties = null;\n this._headerValues = undefined;\n this._headerRowIndex = 1;\n this._cells = [];\n }\n\n private _fillCellData(\n dataRanges: CellDataRange[]\n ) {\n _.each(dataRanges, (range) => {\n const startRow = range.startRow || 0;\n const startColumn = range.startColumn || 0;\n const numRows = range.rowMetadata.length;\n const numColumns = range.columnMetadata.length;\n\n // update cell data for entire range\n for (let i = 0; i < numRows; i++) {\n const actualRow = startRow + i;\n for (let j = 0; j < numColumns; j++) {\n const actualColumn = startColumn + j;\n\n // if the row has not been initialized yet, do it\n if (!this._cells[actualRow]) this._cells[actualRow] = [];\n\n // see if the response includes some info for the cell\n const cellData = _.get(range, `rowData[${i}].values[${j}]`);\n\n // update the cell object or create it\n if (this._cells[actualRow][actualColumn]) {\n this._cells[actualRow][actualColumn]._updateRawData(cellData);\n } else {\n this._cells[actualRow][actualColumn] = new GoogleSpreadsheetCell(\n this,\n actualRow,\n actualColumn,\n cellData\n );\n }\n }\n }\n\n // update row metadata\n for (let i = 0; i < range.rowMetadata.length; i++) {\n this._rowMetadata[startRow + i] = range.rowMetadata[i];\n }\n // update column metadata\n for (let i = 0; i < range.columnMetadata.length; i++) {\n this._columnMetadata[startColumn + i] = range.columnMetadata[i];\n }\n });\n }\n\n // TODO: make this handle A1 ranges as well?\n private _addSheetIdToRange(range: GridRangeWithOptionalWorksheetId) {\n if (range.sheetId && range.sheetId !== this.sheetId) {\n throw new Error('Leave sheet ID blank or set to matching ID of this sheet');\n }\n return {\n ...range,\n sheetId: this.sheetId,\n };\n }\n\n\n // PROPERTY GETTERS //////////////////////////////////////////////////////////////////////////////\n\n private _getProp<T extends keyof WorksheetProperties>(param: T): WorksheetProperties[T] {\n this._ensureInfoLoaded();\n // see note about asserting info loaded on GoogleSpreasheet\n return this._rawProperties![param];\n }\n // eslint-disable-line no-unused-vars\n private _setProp<T extends keyof WorksheetProperties>(_param: T, _newVal: WorksheetProperties[T]) {\n throw new Error('Do not update directly - use `updateProperties()`');\n }\n\n get sheetId() { return this._getProp('sheetId'); }\n get title() { return this._getProp('title'); }\n get index() { return this._getProp('index'); }\n get sheetType() { return this._getProp('sheetType'); }\n get gridProperties() { return this._getProp('gridProperties'); }\n get hidden() { return this._getProp('hidden'); }\n get tabColor() { return this._getProp('tabColor'); }\n get rightToLeft() { return this._getProp('rightToLeft'); }\n get protectedRanges() { return this._protectedRanges; }\n private get _headerRange() {\n return `A${this._headerRowIndex}:${this.lastColumnLetter}${this._headerRowIndex}`;\n }\n\n set sheetId(newVal: WorksheetProperties['sheetId']) { this._setProp('sheetId', newVal); }\n set title(newVal: WorksheetProperties['title']) { this._setProp('title', newVal); }\n set index(newVal: WorksheetProperties['index']) { this._setProp('index', newVal); }\n set sheetType(newVal: WorksheetProperties['sheetType']) { this._setProp('sheetType', newVal); }\n set gridProperties(newVal: WorksheetProperties['gridProperties']) { this._setProp('gridProperties', newVal); }\n set hidden(newVal: WorksheetProperties['hidden']) { this._setProp('hidden', newVal); }\n set tabColor(newVal: WorksheetProperties['tabColor']) { this._setProp('tabColor', newVal); }\n set rightToLeft(newVal: WorksheetProperties['rightToLeft']) { this._setProp('rightToLeft', newVal); }\n\n get rowCount() {\n this._ensureInfoLoaded();\n return this.gridProperties.rowCount;\n }\n get columnCount() {\n this._ensureInfoLoaded();\n return this.gridProperties.columnCount;\n }\n\n get a1SheetName() { return `'${this.title.replace(/'/g, \"''\")}'`; }\n get encodedA1SheetName() { return encodeURIComponent(this.a1SheetName); }\n get lastColumnLetter() {\n // TODO: double check behaviour if data not loaded\n return this.columnCount ? columnToLetter(this.columnCount) : '';\n }\n\n\n // CELLS-BASED INTERACTIONS //////////////////////////////////////////////////////////////////////\n\n get cellStats() {\n let allCells = _.flatten(this._cells);\n allCells = _.compact(allCells);\n return {\n nonEmpty: _.filter(allCells, (c) => c.value).length,\n loaded: allCells.length,\n total: this.rowCount * this.columnCount,\n };\n }\n\n getCellByA1(a1Address: A1Address) {\n const split = a1Address.match(/([A-Z]+)([0-9]+)/);\n if (!split) throw new Error(`Cell address \"${a1Address}\" not valid`);\n const columnIndex = letterToColumn(split[1]);\n const rowIndex = parseInt(split[2]);\n return this.getCell(rowIndex - 1, columnIndex - 1);\n }\n\n getCell(rowIndex: RowIndex, columnIndex: ColumnIndex) {\n if (rowIndex < 0 || columnIndex < 0) throw new Error('Min coordinate is 0, 0');\n if (rowIndex >= this.rowCount || columnIndex >= this.columnCount) {\n throw new Error(`Out of bounds, sheet is ${this.rowCount} by ${this.columnCount}`);\n }\n\n if (!_.get(this._cells, `[${rowIndex}][${columnIndex}]`)) {\n throw new Error('This cell has not been loaded yet');\n }\n return this._cells[rowIndex][columnIndex];\n }\n\n async loadCells(sheetFilters?: DataFilterWithoutWorksheetId | DataFilterWithoutWorksheetId[]) {\n // load the whole sheet\n if (!sheetFilters) return this._spreadsheet.loadCells(this.a1SheetName);\n\n const filtersArray = _.isArray(sheetFilters) ? sheetFilters : [sheetFilters];\n const filtersArrayWithSheetId: DataFilter[] = _.map(filtersArray, (filter) => {\n // add sheet name to A1 ranges\n if (_.isString(filter)) {\n if (filter.startsWith(this.a1SheetName)) return filter;\n return `${this.a1SheetName}!${filter}`;\n }\n if (_.isObject(filter)) {\n // pass through developer metadata filters without adding sheetId\n if ('developerMetadataLookup' in filter) {\n return filter;\n }\n\n // check if the user passed in a sheet id\n const filterAny = filter as any;\n if (filterAny.sheetId && filterAny.sheetId !== this.sheetId) {\n throw new Error('Leave sheet ID blank or set to matching ID of this sheet');\n }\n\n return { sheetId: this.sheetId, ...filter };\n }\n throw new Error('Each filter must be a A1 range string or gridrange object');\n });\n return this._spreadsheet.loadCells(filtersArrayWithSheetId);\n }\n\n async saveUpdatedCells() {\n const cellsToSave = _.filter(_.flatten(this._cells), { _isDirty: true });\n if (cellsToSave.length) {\n await this.saveCells(cellsToSave);\n }\n // TODO: do we want to return stats? or the cells that got updated?\n }\n\n async saveCells(cellsToUpdate: GoogleSpreadsheetCell[]) {\n // we send an individual \"updateCells\" request for each cell\n // because the fields that are udpated for each group are the same\n // and we dont want to accidentally overwrite something\n const requests = _.map(cellsToUpdate, (cell) => cell._getUpdateRequest());\n const responseRanges = _.map(cellsToUpdate, (c) => `${this.a1SheetName}!${c.a1Address}`);\n\n // if nothing is being updated the request returned is just `null`\n // so we make sure at least 1 request is valid - otherwise google throws a 400\n if (!_.compact(requests).length) {\n throw new Error('At least one cell must have something to update');\n }\n\n await this._spreadsheet._makeBatchUpdateRequest(requests, responseRanges);\n }\n\n // SAVING THIS FOR FUTURE USE\n // puts the cells that need updating into batches\n // async updateCellsByBatches() {\n // // saving this code, but it's problematic because each group must have the same update fields\n // const cellsByRow = _.groupBy(cellsToUpdate, 'rowIndex');\n // const groupsToSave = [];\n // _.each(cellsByRow, (cells, rowIndex) => {\n // let cellGroup = [];\n // _.each(cells, (c) => {\n // if (!cellGroup.length) {\n // cellGroup.push(c);\n // } else if (\n // cellGroup[cellGroup.length - 1].columnIndex ===\n // c.columnIndex - 1\n // ) {\n // cellGroup.push(c);\n // } else {\n // groupsToSave.push(cellGroup);\n // cellGroup = [];\n // }\n // });\n // groupsToSave.push(cellGroup);\n // });\n // const requests = _.map(groupsToSave, (cellGroup) => ({\n // updateCells: {\n // rows: [\n // {\n // values: _.map(cellGroup, (cell) => ({\n // ...cell._draftData.value && {\n // userEnteredValue: { [cell._draftData.valueType]: cell._draftData.value },\n // },\n // ...cell._draftData.note !== undefined && {\n // note: cell._draftData.note ,\n // },\n // ...cell._draftData.userEnteredFormat && {\n // userEnteredValue: cell._draftData.userEnteredFormat,\n // },\n // })),\n // },\n // ],\n // fields: 'userEnteredValue,note,userEnteredFormat',\n // start: {\n // sheetId: this.sheetId,\n // rowIndex: cellGroup[0].rowIndex,\n // columnIndex: cellGroup[0].columnIndex,\n // },\n // },\n // }));\n // const responseRanges = _.map(groupsToSave, (cellGroup) => {\n // let a1Range = cellGroup[0].a1Address;\n // if (cellGroup.length > 1)\n // a1Range += `:${cellGroup[cellGroup.length - 1].a1Address}`;\n // return `${cellGroup[0]._sheet.a1SheetName}!${a1Range}`;\n // });\n // }\n\n // ROW BASED FUNCTIONS ///////////////////////////////////////////////////////////////////////////\n\n async _ensureHeaderRowLoaded() {\n if (!this._headerValues) {\n await this.loadHeaderRow();\n }\n }\n\n async loadHeaderRow(headerRowIndex?: number) {\n if (headerRowIndex !== undefined) this._headerRowIndex = headerRowIndex;\n const rows = await this.getCellsInRange(this._headerRange);\n this._processHeaderRow(rows);\n }\n\n private _processHeaderRow(rows: any[]) {\n if (!rows) {\n throw new Error('No values in the header row - fill the first row with header values before trying to interact with rows');\n }\n this._headerValues = _.map(rows[0], (header) => header?.trim());\n if (!_.compact(this.headerValues).length) {\n throw new Error('All your header cells are blank - fill the first row with header values before trying to interact with rows');\n }\n checkForDuplicateHeaders(this.headerValues);\n }\n\n async setHeaderRow(headerValues: string[], headerRowIndex?: number) {\n if (!headerValues) return;\n if (headerValues.length > this.columnCount) {\n throw new Error(`Sheet is not large enough to fit ${headerValues.length} columns. Resize the sheet first.`);\n }\n const trimmedHeaderValues = _.map(headerValues, (h) => h?.trim());\n checkForDuplicateHeaders(trimmedHeaderValues);\n\n if (!_.compact(trimmedHeaderValues).length) {\n throw new Error('All your header cells are blank -');\n }\n\n if (headerRowIndex) this._headerRowIndex = headerRowIndex;\n\n const response = await this._spreadsheet.sheetsApi.put(\n `values/${this.encodedA1SheetName}!${this._headerRowIndex}:${this._headerRowIndex}`,\n {\n searchParams: {\n valueInputOption: 'USER_ENTERED', // other option is RAW\n includeValuesInResponse: true,\n },\n json: {\n range: `${this.a1SheetName}!${this._headerRowIndex}:${this._headerRowIndex}`,\n majorDimension: 'ROWS',\n values: [[\n ...trimmedHeaderValues,\n // pad the rest of the row with empty values to clear them all out\n ..._.times(this.columnCount - trimmedHeaderValues.length, () => ''),\n ]],\n },\n }\n );\n const data = await response.json<any>();\n this._headerValues = data.updatedData.values[0];\n }\n\n // TODO: look at these types\n async addRows(\n rows: RawRowData[],\n options: AddRowOptions = {}\n ) {\n // adds multiple rows in one API interaction using the append endpoint\n\n // each row can be an array or object\n // an array is just cells\n // ex: ['column 1', 'column 2', 'column 3']\n // an object must use the header row values as keys\n // ex: { col1: 'column 1', col2: 'column 2', col3: 'column 3' }\n\n // google bug that does not handle colons in sheet names\n // see https://issuetracker.google.com/issues/150373119\n if (this.title.includes(':')) {\n 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.');\n }\n\n if (!_.isArray(rows)) throw new Error('You must pass in an array of row values to append');\n\n await this._ensureHeaderRowLoaded();\n\n // convert each row into an array of cell values rather than the key/value object\n const rowsAsArrays: RawRowData[] = [];\n _.each(rows, (row) => {\n let rowAsArray;\n if (_.isArray(row)) {\n rowAsArray = row;\n } else if (_.isObject(row)) {\n rowAsArray = [];\n for (let i = 0; i < this.headerValues.length; i++) {\n const propName = this.headerValues[i];\n rowAsArray[i] = row[propName];\n }\n } else {\n throw new Error('Each row must be an object or an array');\n }\n rowsAsArrays.push(rowAsArray);\n });\n\n const response = await this._spreadsheet.sheetsApi.post(\n `values/${this.encodedA1SheetName}!A${this._headerRowIndex}:append`,\n {\n searchParams: {\n valueInputOption: options.raw ? 'RAW' : 'USER_ENTERED',\n insertDataOption: options.insert ? 'INSERT_ROWS' : 'OVERWRITE',\n includeValuesInResponse: true,\n },\n json: {\n values: rowsAsArrays,\n },\n }\n );\n\n // extract the new row number from the A1-notation data range in the response\n // ex: in \"'Sheet8!A2:C2\" -- we want the `2`\n const data = await response.json<any>();\n const { updatedRange } = data.updates;\n let rowNumber = updatedRange.match(/![A-Z]+([0-9]+):?/)[1];\n rowNumber = parseInt(rowNumber);\n\n\n this._ensureInfoLoaded();\n // if new rows were added, we need update sheet.rowRount\n if (options.insert) {\n this._rawProperties!.gridProperties.rowCount += rows.length;\n } else if (rowNumber + rows.length > this.rowCount) {\n // have to subtract 1 since one row was inserted at rowNumber\n this._rawProperties!.gridProperties.rowCount = rowNumber + rows.length - 1;\n }\n\n return _.map(data.updates.updatedData.values, (rowValues) => {\n const row = new GoogleSpreadsheetRow(this, rowNumber++, rowValues);\n return row;\n });\n }\n\n /**\n * add a single row - see addRows for more info\n */\n async addRow(\n rowValues: RawRowData,\n options?: AddRowOptions\n ) {\n const rows = await this.addRows([rowValues], options);\n return rows[0];\n }\n\n\n private _rowCache: GoogleSpreadsheetRow[] = [];\n async getRows<T extends Record<string, any>>(\n options?: {\n /** skip first N rows */\n offset?: number,\n /** limit number of rows fetched */\n limit?: number,\n }\n ) {\n // https://developers.google.com/sheets/api/guides/migration\n // v4 API does not have equivalents for the row-order query parameters provided\n // Reverse-order is trivial; simply process the returned values array in reverse order.\n // Order by column is not supported for reads, but it is possible to sort the data then read\n\n // v4 API does not currently have a direct equivalent for the Sheets API v3 structured queries\n // However, you can retrieve the relevant data and sort through it as needed in your application\n const offset = options?.offset || 0;\n const limit = options?.limit || this.rowCount - 1;\n\n const firstRow = 1 + this._headerRowIndex + offset;\n const lastRow = firstRow + limit - 1; // inclusive so we subtract 1\n\n let rawRows;\n if (this._headerValues) {\n const lastColumn = columnToLetter(this.headerValues.length);\n rawRows = await this.getCellsInRange(\n `A${firstRow}:${lastColumn}${lastRow}`\n );\n } else {\n const result = await this.batchGetCellsInRange([this._headerRange,\n `A${firstRow}:${this.lastColumnLetter}${lastRow}`]);\n this._processHeaderRow(result[0]);\n rawRows = result[1];\n }\n\n if (!rawRows) return [];\n\n const rows = [];\n let rowNum = firstRow;\n for (let i = 0; i < rawRows.length; i++) {\n const row = new GoogleSpreadsheetRow<T>(this, rowNum++, rawRows[i]);\n this._rowCache[row.rowNumber] = row;\n rows.push(row);\n }\n return rows;\n }\n\n /**\n * @internal\n * Used internally to update row numbers after deleting rows.\n * Should not be called directly.\n * */\n _shiftRowCache(deletedRowNumber: number) {\n delete this._rowCache[deletedRowNumber];\n this._rowCache.forEach((row) => {\n if (row.rowNumber > deletedRowNumber) {\n row._updateRowNumber(row.rowNumber - 1);\n }\n });\n }\n\n /**\n * @internal\n * Used internally to update row numbers after deleting multiple rows.\n * Should not be called directly.\n * */\n _shiftRowCacheBulk(startIndex: number, endIndex: number) {\n const numDeleted = endIndex - startIndex;\n // Convert from 0-based indices to 1-based row numbers\n const startRow = startIndex + 1;\n const endRow = endIndex;\n\n // Mark rows in the deleted range as deleted, then remove from cache\n for (let rowNum = startRow; rowNum <= endRow; rowNum++) {\n const row = this._rowCache[rowNum];\n if (row) {\n row._markDeleted(); // Mark as deleted\n }\n delete this._rowCache[rowNum];\n }\n\n // Shift rows after the deleted range\n this._rowCache.forEach((row) => {\n if (row.rowNumber > endRow) {\n row._updateRowNumber(row.rowNumber - numDeleted);\n }\n });\n }\n\n /**\n * @internal\n * Used internally to shift cell cache after deleting rows.\n * Should not be called directly.\n * */\n _shiftCellCacheRows(startIndex: number, endIndex: number) {\n const numDeleted = endIndex - startIndex;\n\n // Mark cells in the deleted row range as deleted, then remove from cache\n for (let rowIndex = startIndex; rowIndex < endIndex; rowIndex++) {\n const row = this._cells[rowIndex];\n if (row) {\n row.forEach((cell) => {\n if (cell) cell._markDeleted();\n });\n }\n delete this._cells[rowIndex];\n }\n\n // Collect rows that need to be shifted\n const rowsToShift: Array<{ oldRowIndex: number, cells: any[] }> = [];\n for (let rowIndex = endIndex; rowIndex < this._cells.length; rowIndex++) {\n if (this._cells[rowIndex]) {\n rowsToShift.push({ oldRowIndex: rowIndex, cells: this._cells[rowIndex] });\n }\n }\n\n // Clear old positions and update to new positions\n rowsToShift.forEach(({ oldRowIndex, cells }) => {\n delete this._cells[oldRowIndex];\n const newRowIndex = oldRowIndex - numDeleted;\n this._cells[newRowIndex] = cells;\n // Update each cell's internal row index\n cells.forEach((cell, colIndex) => {\n if (cell) cell._updateIndices(newRowIndex, colIndex);\n });\n });\n }\n\n /**\n * @internal\n * Used internally to shift cell cache after deleting columns.\n * Should not be called directly.\n * */\n _shiftCellCacheColumns(startIndex: number, endIndex: number) {\n const numDeleted = endIndex - startIndex;\n\n // For each row, delete cells in the deleted column range and shift remaining\n this._cells.forEach((row, rowIndex) => {\n if (!row) return;\n\n // Mark cells in the deleted column range as deleted, then remove from cache\n for (let colIndex = startIndex; colIndex < endIndex; colIndex++) {\n const cell = row[colIndex];\n if (cell) cell._markDeleted();\n delete row[colIndex];\n }\n\n // Collect cells that need to be shifted\n const cellsToShift: Array<{ oldColIndex: number, cell: any }> = [];\n for (let colIndex = endIndex; colIndex < row.length; colIndex++) {\n if (row[colIndex]) {\n cellsToShift.push({ oldColIndex: colIndex, cell: row[colIndex] });\n }\n }\n\n // Clear old positions and update to new positions\n cellsToShift.forEach(({ oldColIndex, cell }) => {\n delete row[oldColIndex];\n const newColIndex = oldColIndex - numDeleted;\n row[newColIndex] = cell;\n // Update cell's internal column index\n cell._updateIndices(rowIndex, newColIndex);\n });\n });\n }\n\n async clearRows(\n options?: {\n start?: number,\n end?: number,\n }\n ) {\n // default to first row after header\n const startRowIndex = options?.start || this._headerRowIndex + 1;\n const endRowIndex = options?.end || this.rowCount;\n await this._spreadsheet.sheetsApi.post(`values/${this.encodedA1SheetName}!${startRowIndex}:${endRowIndex}:clear`);\n this._rowCache.forEach((row) => {\n if (row.rowNumber >= startRowIndex && row.rowNumber <= endRowIndex) row._clearRowData();\n });\n }\n\n // BASIC PROPS ///////////////////////////////////////////////////////////////////////////////////\n /** @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateSheetPropertiesRequest */\n async updateProperties(properties: Partial<Omit<WorksheetProperties, 'sheetId'>>) {\n // Request type = `updateSheetProperties`\n\n return this._makeSingleUpdateRequest('updateSheetProperties', {\n properties: {\n sheetId: this.sheetId,\n ...properties,\n },\n fields: getFieldMask(properties),\n });\n }\n\n /**\n * passes through the call to updateProperties to update only the gridProperties object\n */\n async updateGridProperties(\n gridProperties: Partial<WorksheetGridProperties>\n ) {\n return this.updateProperties({ gridProperties: gridProperties as WorksheetGridProperties });\n }\n\n /**\n * resize, internally just calls updateGridProperties\n */\n async resize(\n gridProperties: Pick<WorksheetGridProperties, 'rowCount' | 'columnCount'>\n ) {\n return this.updateGridProperties(gridProperties);\n }\n\n /**\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#updatedimensionpropertiesrequest\n */\n async updateDimensionProperties(\n columnsOrRows: WorksheetDimension,\n properties: Partial<WorksheetDimensionProperties>,\n bounds: Partial<DimensionRangeIndexes>\n ) {\n // Request type = `updateDimensionProperties`\n\n Object.keys(properties);\n\n return this._makeSingleUpdateRequest('updateDimensionProperties', {\n range: {\n sheetId: this.sheetId,\n dimension: columnsOrRows,\n ...bounds,\n },\n properties,\n fields: getFieldMask(properties as any),\n });\n }\n\n // OTHER /////////////////////////////////////////////////////////////////////////////////////////\n\n // this uses the \"values\" getter and does not give all the info about the cell contents\n // it is used internally when loading header cells\n async getCellsInRange(a1Range: A1Range, options?: GetValuesRequestOptions) {\n const response = await this._spreadsheet.sheetsApi.get(`values/${this.encodedA1SheetName}!${a1Range}`, {\n searchParams: options,\n });\n const data = await response.json<any>();\n return data.values;\n }\n\n async batchGetCellsInRange(a1Ranges: A1Range[], options?: GetValuesRequestOptions) {\n const ranges = a1Ranges.map((r) => `ranges=${this.encodedA1SheetName}!${r}`).join('&');\n const response = await this._spreadsheet.sheetsApi.get(`values:batchGet?${ranges}`, {\n searchParams: options,\n });\n const data = await response.json<any>();\n return data.valueRanges.map((r: any) => r.values);\n }\n\n /**\n * Updates an existing named range\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateNamedRangeRequest\n */\n async updateNamedRange(\n /** ID of the named range to update */\n namedRangeId: string,\n /** The named range properties to update */\n namedRange: Partial<{ name: string, range: GridRangeWithOptionalWorksheetId }>,\n /** Field mask specifying which properties to update */\n fields: string\n ) {\n return this._makeSingleUpdateRequest('updateNamedRange', {\n namedRange: {\n namedRangeId,\n ...namedRange.name && { name: namedRange.name },\n ...namedRange.range && { range: this._addSheetIdToRange(namedRange.range) },\n },\n fields,\n });\n }\n\n /**\n * Creates a new named range in this worksheet (convenience method that auto-fills sheetId)\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddNamedRangeRequest\n */\n async addNamedRange(\n /** Name of the new named range */\n name: string,\n /** GridRange describing the range (sheetId optional, will be auto-filled) */\n range: GridRangeWithOptionalWorksheetId,\n /** Optional ID for the named range */\n namedRangeId?: string\n ) {\n return this._spreadsheet.addNamedRange(\n name,\n this._addSheetIdToRange(range),\n namedRangeId\n );\n }\n\n /**\n * Deletes a named range (convenience wrapper)\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DeleteNamedRangeRequest\n */\n async deleteNamedRange(\n /** ID of the named range to delete */\n namedRangeId: string\n ) {\n return this._spreadsheet.deleteNamedRange(namedRangeId);\n }\n\n /**\n * Updates all cells in a range with the same cell data\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#RepeatCellRequest\n */\n async repeatCell(\n /** The range to update (sheetId optional) */\n range: GridRangeWithOptionalWorksheetId,\n /** The cell data to repeat across the range */\n cell: any,\n /** Which fields to update (use \"*\" for all fields) */\n fields: string\n ) {\n await this._makeSingleUpdateRequest('repeatCell', {\n range: this._addSheetIdToRange(range),\n cell,\n fields,\n });\n }\n\n /**\n * Auto-fills cells with data following a pattern (like dragging the fill handle)\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AutoFillRequest\n */\n async autoFill(\n /** The range to autofill (detects source location automatically, sheetId optional) or explicit source and destination specification */\n rangeOrSource: GridRangeWithOptionalWorksheetId | SourceAndDestination,\n /** Whether to generate data with the alternate series */\n useAlternateSeries?: boolean\n ) {\n // Check if it's a SourceAndDestination by looking for the 'dimension' property\n const isSourceAndDestination = 'dimension' in rangeOrSource;\n\n await this._makeSingleUpdateRequest('autoFill', {\n ...isSourceAndDestination\n ? {\n sourceAndDestination: {\n ...rangeOrSource,\n source: this._addSheetIdToRange((rangeOrSource as SourceAndDestination).source),\n },\n }\n : { range: this._addSheetIdToRange(rangeOrSource as GridRangeWithOptionalWorksheetId) },\n ...useAlternateSeries !== undefined && { useAlternateSeries },\n });\n }\n\n /**\n * Cuts data from a source range and pastes it to a destination coordinate\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#CutPasteRequest\n */\n async cutPaste(\n /** The source range to cut from (sheetId optional) */\n source: GridRangeWithOptionalWorksheetId,\n /** The top-left coordinate where data should be pasted (sheetId optional) */\n destination: GridCoordinateWithOptionalWorksheetId,\n /** What kind of data to paste (defaults to PASTE_NORMAL) */\n pasteType: PasteType = 'PASTE_NORMAL'\n ) {\n await this._makeSingleUpdateRequest('cutPaste', {\n source: this._addSheetIdToRange(source),\n destination: {\n sheetId: this.sheetId,\n rowIndex: destination.rowIndex,\n columnIndex: destination.columnIndex,\n },\n pasteType,\n });\n }\n\n /**\n * Copies data from a source range and pastes it to a destination range\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#CopyPasteRequest\n */\n async copyPaste(\n /** The source range to copy from (sheetId optional) */\n source: GridRangeWithOptionalWorksheetId,\n /** The destination range to paste to (sheetId optional) */\n destination: GridRangeWithOptionalWorksheetId,\n /** What kind of data to paste (defaults to PASTE_NORMAL) */\n pasteType: PasteType = 'PASTE_NORMAL',\n /** How data should be oriented (defaults to NORMAL) */\n pasteOrientation: PasteOrientation = 'NORMAL'\n ) {\n await this._makeSingleUpdateRequest('copyPaste', {\n source: this._addSheetIdToRange(source),\n destination: this._addSheetIdToRange(destination),\n pasteType,\n pasteOrientation,\n });\n }\n\n // TODO: check types on these ranges\n\n /**\n * Merges all cells in the range\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#MergeCellsRequest\n */\n async mergeCells(\n range: GridRangeWithOptionalWorksheetId,\n mergeType = 'MERGE_ALL'\n ) {\n await this._makeSingleUpdateRequest('mergeCells', {\n mergeType,\n range: this._addSheetIdToRange(range),\n });\n }\n\n /**\n * Unmerges cells in the given range\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UnmergeCellsRequest\n */\n async unmergeCells(\n range: GridRangeWithOptionalWorksheetId\n ) {\n await this._makeSingleUpdateRequest('unmergeCells', {\n range: this._addSheetIdToRange(range),\n });\n }\n\n /**\n * Updates borders for a range\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateBordersRequest\n */\n async updateBorders(\n /** The range whose borders should be updated (sheetId optional) */\n range: GridRangeWithOptionalWorksheetId,\n /** Border styles for top, bottom, left, right, innerHorizontal, innerVertical */\n borders: {\n top?: any,\n bottom?: any,\n left?: any,\n right?: any,\n innerHorizontal?: any,\n innerVertical?: any\n }\n ) {\n await this._makeSingleUpdateRequest('updateBorders', {\n range: this._addSheetIdToRange(range),\n ...borders,\n });\n }\n\n /**\n * Adds a filter view to the sheet\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddFilterViewRequest\n */\n async addFilterView(\n /** The filter view to add (filterViewId is optional and will be auto-generated if not provided) */\n filter: FilterView\n ) {\n return this._makeSingleUpdateRequest('addFilterView', {\n filter,\n });\n }\n\n /**\n * Appends cells after the last row with data in a sheet\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AppendCellsRequest\n */\n async appendCells(\n /** The row data to append */\n rows: any[],\n /** Which fields to update (use \"*\" for all fields) */\n fields: string\n ) {\n await this._makeSingleUpdateRequest('appendCells', {\n sheetId: this.sheetId,\n rows,\n fields,\n });\n }\n\n /**\n * Clears the basic filter on this sheet\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#ClearBasicFilterRequest\n */\n async clearBasicFilter() {\n await this._makeSingleUpdateRequest('clearBasicFilter', {\n sheetId: this.sheetId,\n });\n }\n\n /**\n * Delete rows or columns in a given range\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DeleteDimensionRequest\n */\n async deleteDimension(\n columnsOrRows: WorksheetDimension,\n rangeIndexes: DimensionRangeIndexes\n ) {\n if (!columnsOrRows) throw new Error('You need to specify a dimension. i.e. COLUMNS|ROWS');\n if (!_.isObject(rangeIndexes)) throw new Error('`range` must be an object containing `startIndex` and `endIndex`');\n if (!_.isInteger(rangeIndexes.startIndex) || rangeIndexes.startIndex < 0) throw new Error('range.startIndex must be an integer >=0');\n if (!_.isInteger(rangeIndexes.endIndex) || rangeIndexes.endIndex < 0) throw new Error('range.endIndex must be an integer >=0');\n if (rangeIndexes.endIndex <= rangeIndexes.startIndex) throw new Error('range.endIndex must be greater than range.startIndex');\n\n const result = await this._makeSingleUpdateRequest('deleteDimension', {\n range: {\n sheetId: this.sheetId,\n dimension: columnsOrRows,\n startIndex: rangeIndexes.startIndex,\n endIndex: rangeIndexes.endIndex,\n },\n });\n\n // Update cached rows and cells\n if (columnsOrRows === 'ROWS') {\n this._shiftRowCacheBulk(rangeIndexes.startIndex, rangeIndexes.endIndex);\n this._shiftCellCacheRows(rangeIndexes.startIndex, rangeIndexes.endIndex);\n } else {\n this._shiftCellCacheColumns(rangeIndexes.startIndex, rangeIndexes.endIndex);\n }\n\n return result;\n }\n\n /**\n * Delete rows by index\n */\n async deleteRows(\n /** the start row index (inclusive, 0-based) */\n startIndex: number,\n /** the end row index (exclusive) */\n endIndex: number\n ) {\n return this.deleteDimension('ROWS', { startIndex, endIndex });\n }\n\n /**\n * Delete columns by index\n */\n async deleteColumns(\n /** the start column index (inclusive, 0-based) */\n startIndex: number,\n /** the end column index (exclusive) */\n endIndex: number\n ) {\n return this.deleteDimension('COLUMNS', { startIndex, endIndex });\n }\n\n async deleteEmbeddedObject() {\n // Request type = `deleteEmbeddedObject`\n // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DeleteEmbeddedObjectRequest\n throw new Error('Not implemented yet');\n }\n\n /**\n * Deletes a filter view from the sheet\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DeleteFilterViewRequest\n */\n async deleteFilterView(\n /** The ID of the filter view to delete */\n filterId: Integer\n ) {\n await this._makeSingleUpdateRequest('deleteFilterView', {\n filterId,\n });\n }\n\n /**\n * Duplicates a filter view\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DuplicateFilterViewRequest\n */\n async duplicateFilterView(\n /** The ID of the filter view to duplicate */\n filterId: Integer\n ) {\n await this._makeSingleUpdateRequest('duplicateFilterView', {\n filterId,\n });\n }\n\n /**\n * Duplicate worksheet within the document\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DuplicateSheetRequest\n */\n async duplicate(\n options?: {\n id?: WorksheetId,\n title?: string,\n index?: number,\n }\n ) {\n const response = await this._makeSingleUpdateRequest('duplicateSheet', {\n sourceSheetId: this.sheetId,\n ...options?.index !== undefined && { insertSheetIndex: options.index },\n ...options?.id && { newSheetId: options.id },\n ...options?.title && { newSheetName: options.title },\n });\n const newSheetId = response.properties.sheetId;\n return this._spreadsheet.sheetsById[newSheetId];\n }\n\n /**\n * Finds and replaces text in cells\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#FindReplaceRequest\n */\n async findReplace(\n /** The value to search for */\n find: string,\n /** The value to use as replacement */\n replacement: string,\n /** Search options (matchCase, matchEntireCell, searchByRegex, includeFormulas) */\n options?: {\n matchCase?: boolean,\n matchEntireCell?: boolean,\n searchByRegex?: boolean,\n includeFormulas?: boolean\n },\n /** Optional range to search in (defaults to entire sheet, sheetId optional) */\n range?: GridRangeWithOptionalWorksheetId\n ) {\n await this._makeSingleUpdateRequest('findReplace', {\n find,\n replacement,\n ...options,\n ...range\n ? { range: this._addSheetIdToRange(range) }\n : { sheetId: this.sheetId },\n });\n }\n\n /**\n * Inserts rows or columns at a particular index\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#InsertDimensionRequest\n */\n async insertDimension(\n columnsOrRows: WorksheetDimension,\n rangeIndexes: DimensionRangeIndexes,\n inheritFromBefore?: boolean\n ) {\n if (!columnsOrRows) throw new Error('You need to specify a dimension. i.e. COLUMNS|ROWS');\n if (!_.isObject(rangeIndexes)) throw new Error('`range` must be an object containing `startIndex` and `endIndex`');\n if (!_.isInteger(rangeIndexes.startIndex) || rangeIndexes.startIndex < 0) throw new Error('range.startIndex must be an integer >=0');\n if (!_.isInteger(rangeIndexes.endIndex) || rangeIndexes.endIndex < 0) throw new Error('range.endIndex must be an integer >=0');\n if (rangeIndexes.endIndex <= rangeIndexes.startIndex) throw new Error('range.endIndex must be greater than range.startIndex');\n\n // default inheritFromBefore to true - unless inserting in the first row/column\n if (inheritFromBefore === undefined) {\n inheritFromBefore = rangeIndexes.startIndex > 0;\n }\n\n // do not allow inheritFromBefore if inserting at first row/column\n if (inheritFromBefore && rangeIndexes.startIndex === 0) {\n throw new Error('Cannot set inheritFromBefore to true if inserting in first row/column');\n }\n\n return this._makeSingleUpdateRequest('insertDimension', {\n range: {\n sheetId: this.sheetId,\n dimension: columnsOrRows,\n startIndex: rangeIndexes.startIndex,\n endIndex: rangeIndexes.endIndex,\n },\n inheritFromBefore,\n });\n }\n\n /**\n * insert empty cells in a range, shifting existing cells in the specified direction\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#InsertRangeRequest\n */\n async insertRange(\n /** the range to insert new cells into */\n range: GridRangeWithOptionalWorksheetId,\n /** which direction to shift existing cells - ROWS (shift down) or COLUMNS (shift right) */\n shiftDimension: WorksheetDimension\n ) {\n await this._makeSingleUpdateRequest('insertRange', {\n range: this._addSheetIdToRange(range),\n shiftDimension,\n });\n }\n\n /**\n * Moves rows or columns to a different position within the sheet\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#MoveDimensionRequest\n */\n async moveDimension(\n /** Whether to move rows or columns */\n dimension: WorksheetDimension,\n /** The indexes of rows/columns to move */\n source: DimensionRangeIndexes,\n /** Where to move them (calculated before removal) */\n destinationIndex: number\n ) {\n await this._makeSingleUpdateRequest('moveDimension', {\n source: {\n sheetId: this.sheetId,\n dimension,\n startIndex: source.startIndex,\n endIndex: source.endIndex,\n },\n destinationIndex,\n });\n }\n\n async updateEmbeddedObjectPosition() {\n // Request type = `updateEmbeddedObjectPosition`\n // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateEmbeddedObjectPositionRequest\n throw new Error('Not implemented yet');\n }\n\n /**\n * Inserts data into the spreadsheet starting at the specified coordinate\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#PasteDataRequest\n */\n async pasteData(\n /** The coordinate at which the data should start being inserted (sheetId optional) */\n coordinate: GridCoordinateWithOptionalWorksheetId,\n /** The data to insert */\n data: string,\n /** The delimiter in the data */\n delimiter: string,\n /** How the data should be pasted (defaults to PASTE_NORMAL) */\n type: PasteType = 'PASTE_NORMAL'\n ) {\n await this._makeSingleUpdateRequest('pasteData', {\n coordinate: {\n sheetId: this.sheetId,\n rowIndex: coordinate.rowIndex,\n columnIndex: coordinate.columnIndex,\n },\n data,\n delimiter,\n type,\n });\n }\n\n /**\n * Splits a column of text into multiple columns based on a delimiter\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#TextToColumnsRequest\n */\n async textToColumns(\n /** The column to split (must span exactly one column) */\n source: GridRangeWithOptionalWorksheetId,\n /** Type of delimiter to use */\n delimiterType: DelimiterType,\n /** Custom delimiter character (only used when delimiterType is CUSTOM) */\n delimiter?: string\n ) {\n await this._makeSingleUpdateRequest('textToColumns', {\n source: this._addSheetIdToRange(source),\n delimiterType,\n ...delimiter && { delimiter },\n });\n }\n\n /**\n * Updates properties of a filter view\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateFilterViewRequest\n */\n async updateFilterView(\n /** The new properties of the filter view */\n filter: FilterView,\n /** The fields that should be updated (use \"*\" to update all fields) */\n fields: string\n ) {\n await this._makeSingleUpdateRequest('updateFilterView', {\n filter,\n fields,\n });\n }\n\n /**\n * Deletes a range of cells and shifts remaining cells\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DeleteRangeRequest\n */\n async deleteRange(\n /** The range of cells to delete (sheetId optional) */\n range: GridRangeWithOptionalWorksheetId,\n /** How remaining cells should shift (ROWS = up, COLUMNS = left) */\n shiftDimension: WorksheetDimension\n ) {\n await this._makeSingleUpdateRequest('deleteRange', {\n range: this._addSheetIdToRange(range),\n shiftDimension,\n });\n }\n\n /**\n * Appends rows or columns to the end of a sheet\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AppendDimensionRequest\n */\n async appendDimension(\n /** Whether rows or columns should be appended */\n dimension: WorksheetDimension,\n /** The number of rows or columns to append */\n length: number\n ) {\n await this._makeSingleUpdateRequest('appendDimension', {\n sheetId: this.sheetId,\n dimension,\n length,\n });\n }\n\n /**\n * Adds a new conditional formatting rule at the given index\n * All subsequent rules' indexes are incremented\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddConditionalFormatRuleRequest\n */\n async addConditionalFormatRule(\n /** The rule to add */\n rule: ConditionalFormatRule,\n /** The zero-based index where the rule should be inserted */\n index: Integer\n ) {\n await this._makeSingleUpdateRequest('addConditionalFormatRule', {\n rule,\n index,\n });\n }\n\n /**\n * Updates a conditional format rule at the given index, or moves a conditional format rule to another index\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateConditionalFormatRuleRequest\n */\n async updateConditionalFormatRule(\n /** Either provide `rule` to replace the rule, or `newIndex` and `sheetId` to move it */\n options: {\n /** The zero-based index of the rule */\n index: Integer;\n /** The rule that should replace the rule at the given index (mutually exclusive with newIndex) */\n rule?: ConditionalFormatRule;\n /** The zero-based new index the rule should end up at (mutually exclusive with rule, requires sheetId) */\n newIndex?: Integer;\n /** The sheet of the rule to move (required if newIndex is set) */\n sheetId?: WorksheetId;\n }\n ) {\n await this._makeSingleUpdateRequest('updateConditionalFormatRule', options);\n }\n\n /**\n * Deletes a conditional format rule at the given index\n * All subsequent rules' indexes are decremented\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DeleteConditionalFormatRuleRequest\n */\n async deleteConditionalFormatRule(\n /** The zero-based index of the rule to be deleted */\n index: Integer,\n /** The sheet the rule is being deleted from (defaults to this sheet) */\n sheetId?: WorksheetId\n ) {\n await this._makeSingleUpdateRequest('deleteConditionalFormatRule', {\n index,\n sheetId: sheetId ?? this.sheetId,\n });\n }\n\n /**\n * Sorts data in rows based on sort order per column\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#SortRangeRequest\n */\n async sortRange(\n /** The range to sort (sheetId optional) */\n range: GridRangeWithOptionalWorksheetId,\n /** Array of sort specifications (later specs used when values are equal) */\n sortSpecs: SortSpec[]\n ) {\n await this._makeSingleUpdateRequest('sortRange', {\n range: this._addSheetIdToRange(range),\n sortSpecs,\n });\n }\n\n /**\n * Sets (or unsets) a data validation rule to every cell in the range\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#SetDataValidationRequest\n */\n async setDataValidation(\n range: GridRangeWithOptionalWorksheetId,\n /** data validation rule object, or set to false to clear an existing rule */\n rule: DataValidationRule | false\n ) {\n return this._makeSingleUpdateRequest('setDataValidation', {\n range: {\n sheetId: this.sheetId,\n ...range,\n },\n ...rule && { rule },\n });\n }\n\n /**\n * Sets the basic filter on this sheet\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#SetBasicFilterRequest\n */\n async setBasicFilter(\n /** The basic filter configuration (range will auto-fill sheetId if not provided) */\n filter: {\n range?: GridRangeWithOptionalWorksheetId,\n sortSpecs?: SortSpec[],\n filterSpecs?: any[]\n }\n ) {\n await this._makeSingleUpdateRequest('setBasicFilter', {\n filter: {\n ...filter,\n ...filter.range && { range: this._addSheetIdToRange(filter.range) },\n },\n });\n }\n\n /**\n * add a new protected range to the sheet\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddProtectedRangeRequest\n */\n async addProtectedRange(\n protectedRange: ProtectedRange\n ) {\n if (!protectedRange.range && !protectedRange.namedRangeId) {\n throw new Error('No range specified: either range or namedRangeId is required');\n }\n return this._makeSingleUpdateRequest('addProtectedRange', {\n protectedRange,\n });\n }\n\n /**\n * update an existing protected range\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateProtectedRangeRequest\n */\n async updateProtectedRange(\n protectedRangeId: Integer,\n protectedRange: Partial<ProtectedRange>\n ) {\n return this._makeSingleUpdateRequest('updateProtectedRange', {\n protectedRange: { protectedRangeId, ...protectedRange },\n fields: getFieldMask(protectedRange as Record<string, unknown>),\n });\n }\n\n /**\n * delete a protected range by ID\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DeleteProtectedRangeRequest\n */\n async deleteProtectedRange(\n protectedRangeId: Integer\n ) {\n return this._makeSingleUpdateRequest('deleteProtectedRange', {\n protectedRangeId,\n });\n }\n\n /**\n * auto-resize rows or columns to fit their contents\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AutoResizeDimensionsRequest\n */\n async autoResizeDimensions(\n /** which dimension to auto-resize */\n columnsOrRows: WorksheetDimension,\n /** start and end indexes (optional, defaults to all) */\n rangeIndexes?: DimensionRangeIndexes\n ) {\n return this._makeSingleUpdateRequest('autoResizeDimensions', {\n dimensions: {\n sheetId: this.sheetId,\n dimension: columnsOrRows,\n ...rangeIndexes,\n },\n });\n }\n\n async addChart() {\n // Request type = `addChart`\n // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddChartRequest\n throw new Error('Not implemented yet');\n }\n\n async updateChartSpec() {\n // Request type = `updateChartSpec`\n // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateChartSpecRequest\n throw new Error('Not implemented yet');\n }\n\n /**\n * Updates properties of a banded range\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateBandingRequest\n */\n async updateBanding(\n /** The banded range to update with the new properties */\n bandedRange: BandedRange,\n /** The fields that should be updated (use \"*\" to update all fields) */\n fields: string\n ) {\n await this._makeSingleUpdateRequest('updateBanding', {\n bandedRange,\n fields,\n });\n }\n\n /**\n * Adds a new banded range to the sheet\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddBandingRequest\n */\n async addBanding(\n /** The banded range to add (bandedRangeId is optional and will be auto-generated if not provided) */\n bandedRange: BandedRange\n ) {\n return this._makeSingleUpdateRequest('addBanding', {\n bandedRange,\n });\n }\n\n /**\n * Deletes a banded range from the sheet\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DeleteBandingRequest\n */\n async deleteBanding(\n /** The ID of the banded range to delete */\n bandedRangeId: Integer\n ) {\n await this._makeSingleUpdateRequest('deleteBanding', {\n bandedRangeId,\n });\n }\n\n /**\n * Creates developer metadata\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#CreateDeveloperMetadataRequest\n */\n async createDeveloperMetadata(\n /** The developer metadata to create */\n developerMetadata: DeveloperMetadata\n ) {\n return this._makeSingleUpdateRequest('createDeveloperMetadata', {\n developerMetadata,\n });\n }\n\n /**\n * Updates developer metadata that matches the specified filters\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateDeveloperMetadataRequest\n */\n async updateDeveloperMetadata(\n /** The filters matching the developer metadata entries to update */\n dataFilters: DataFilterObject[],\n /** The value that all metadata matched by the filters will be updated to */\n developerMetadata: DeveloperMetadata,\n /** The fields that should be updated (use \"*\" to update all fields) */\n fields: string\n ) {\n await this._makeSingleUpdateRequest('updateDeveloperMetadata', {\n dataFilters,\n developerMetadata,\n fields,\n });\n }\n\n /**\n * Deletes developer metadata that matches the specified filter\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DeleteDeveloperMetadataRequest\n */\n async deleteDeveloperMetadata(\n /** The filter describing the criteria used to select which developer metadata to delete */\n dataFilter: DataFilterObject\n ) {\n await this._makeSingleUpdateRequest('deleteDeveloperMetadata', {\n dataFilter,\n });\n }\n\n /**\n * Randomizes the order of rows in a range\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#RandomizeRangeRequest\n */\n async randomizeRange(\n /** The range to randomize (sheetId optional) */\n range: GridRangeWithOptionalWorksheetId\n ) {\n await this._makeSingleUpdateRequest('randomizeRange', {\n range: this._addSheetIdToRange(range),\n });\n }\n\n async addDimensionGroup() {\n // Request type = `addDimensionGroup`\n // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddDimensionGroupRequest\n throw new Error('Not implemented yet');\n }\n\n async deleteDimensionGroup() {\n // Request type = `deleteDimensionGroup`\n // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DeleteDimensionGroupRequest\n throw new Error('Not implemented yet');\n }\n\n async updateDimensionGroup() {\n // Request type = `updateDimensionGroup`\n // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateDimensionGroupRequest\n throw new Error('Not implemented yet');\n }\n\n /**\n * Trims whitespace from the start and end of each cell's text\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#TrimWhitespaceRequest\n */\n async trimWhitespace(\n /** The range whose cells to trim (sheetId optional) */\n range: GridRangeWithOptionalWorksheetId\n ) {\n await this._makeSingleUpdateRequest('trimWhitespace', {\n range: this._addSheetIdToRange(range),\n });\n }\n\n /**\n * Removes duplicate rows from a range based on specified columns\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DeleteDuplicatesRequest\n */\n async deleteDuplicates(\n /** The range to remove duplicates from (sheetId optional) */\n range: GridRangeWithOptionalWorksheetId,\n /** Columns to check for duplicates (if empty, all columns are used) */\n comparisonColumns?: DimensionRange[]\n ) {\n await this._makeSingleUpdateRequest('deleteDuplicates', {\n range: this._addSheetIdToRange(range),\n ...comparisonColumns && { comparisonColumns },\n });\n }\n\n async addSlicer() {\n // Request type = `addSlicer`\n // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddSlicerRequest\n throw new Error('Not implemented yet');\n }\n\n async updateSlicerSpec() {\n // Request type = `updateSlicerSpec`\n // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateSlicerSpecRequest\n throw new Error('Not implemented yet');\n }\n\n /**\n * delete this worksheet\n */\n async delete() {\n return this._spreadsheet.deleteSheet(this.sheetId);\n }\n\n /**\n * copies this worksheet into another document/spreadsheet\n *\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.sheets/copyTo\n */\n async copyToSpreadsheet(\n destinationSpreadsheetId: SpreadsheetId\n ) {\n const req = this._spreadsheet.sheetsApi.post(`sheets/${this.sheetId}:copyTo`, {\n json: {\n destinationSpreadsheetId,\n },\n });\n const data = await req.json<any>();\n return data;\n }\n\n /**\n * clear data in the sheet - either the entire sheet or a specific range\n */\n async clear(\n /** optional A1 range to clear - defaults to entire sheet */\n a1Range?: A1Range\n ) {\n const range = a1Range ? `!${a1Range}` : '';\n // sheet name without ie 'sheet1' rather than 'sheet1'!A1:B5 is all cells\n await this._spreadsheet.sheetsApi.post(`values/${this.encodedA1SheetName}${range}:clear`);\n this.resetLocalCache(true);\n }\n\n /**\n * exports worksheet as CSV file (comma-separated values)\n */\n async downloadAsCSV(): Promise<ArrayBuffer>;\n async downloadAsCSV(returnStreamInsteadOfBuffer: false): Promise<ArrayBuffer>;\n async downloadAsCSV(returnStreamInsteadOfBuffer: true): Promise<ReadableStream>;\n async downloadAsCSV(returnStreamInsteadOfBuffer = false) {\n return this._spreadsheet._downloadAs('csv', this.sheetId, returnStreamInsteadOfBuffer);\n }\n /**\n * exports worksheet as TSC file (tab-separated values)\n */\n async downloadAsTSV(): Promise<ArrayBuffer>;\n async downloadAsTSV(returnStreamInsteadOfBuffer: false): Promise<ArrayBuffer>;\n async downloadAsTSV(returnStreamInsteadOfBuffer: true): Promise<ReadableStream>;\n async downloadAsTSV(returnStreamInsteadOfBuffer = false) {\n return this._spreadsheet._downloadAs('tsv', this.sheetId, returnStreamInsteadOfBuffer);\n }\n /**\n * exports worksheet as PDF\n */\n async downloadAsPDF(): Promise<ArrayBuffer>;\n async downloadAsPDF(returnStreamInsteadOfBuffer: false): Promise<ArrayBuffer>;\n async downloadAsPDF(returnStreamInsteadOfBuffer: true): Promise<ReadableStream>;\n async downloadAsPDF(returnStreamInsteadOfBuffer = false) {\n return this._spreadsheet._downloadAs('pdf', this.sheetId, returnStreamInsteadOfBuffer);\n }\n}\n","/** single type to handle all valid auth types */\nexport type GoogleApiAuth =\n // this simple interface should cover all google-auth-library auth methods\n | { getRequestHeaders: () => Promise<any> }\n // used to pass in an API key only\n | { apiKey: string }\n // used to pass in a raw token\n | { token: string };\n\nexport enum AUTH_MODES {\n GOOGLE_AUTH_CLIENT = 'google_auth',\n RAW_ACCESS_TOKEN = 'raw_access_token',\n API_KEY = 'api_key'\n}\n","import ky, { HTTPError, KyInstance, RetryOptions } from 'ky'; // eslint-disable-line import/no-extraneous-dependencies\nimport * as _ from './toolkit';\nimport { GoogleSpreadsheetWorksheet } from './GoogleSpreadsheetWorksheet';\nimport { getFieldMask } from './utils';\nimport {\n DataFilter,\n DataFilterObject,\n DeveloperMetadata,\n GridRange,\n NamedRangeId,\n ProtectedRange,\n SpreadsheetId,\n SpreadsheetProperties,\n WorksheetId,\n WorksheetProperties,\n} from './types/sheets-types';\nimport { PermissionRoles, PermissionsList, PublicPermissionRoles } from './types/drive-types';\nimport { RecursivePartial } from './types/util-types';\nimport { AUTH_MODES, GoogleApiAuth } from './types/auth-types';\n\n\nconst SHEETS_API_BASE_URL = 'https://sheets.googleapis.com/v4/spreadsheets';\nconst DRIVE_API_BASE_URL = 'https://www.googleapis.com/drive/v3/files';\n\nconst EXPORT_CONFIG: Record<string, { singleWorksheet?: boolean }> = {\n html: {},\n zip: {},\n xlsx: {},\n ods: {},\n csv: { singleWorksheet: true },\n tsv: { singleWorksheet: true },\n pdf: { singleWorksheet: true },\n};\ntype ExportFileTypes = keyof typeof EXPORT_CONFIG;\n\n\n\n\nfunction getAuthMode(auth: GoogleApiAuth) {\n if ('getRequestHeaders' in auth) return AUTH_MODES.GOOGLE_AUTH_CLIENT;\n if ('token' in auth && auth.token) return AUTH_MODES.RAW_ACCESS_TOKEN;\n // google-auth-library now has an empty `apiKey` property\n if ('apiKey' in auth && auth.apiKey) return AUTH_MODES.API_KEY;\n throw new Error('Invalid auth');\n}\n\nasync function getRequestAuthConfig(auth: GoogleApiAuth): Promise<{\n headers?: Record<string, string>;\n searchParams?: Record<string, string>\n}> {\n // google-auth-libary methods all can call this method to get the right headers\n // JWT | OAuth2Client | GoogleAuth | Impersonate | AuthClient\n if ('getRequestHeaders' in auth) {\n const headers = await auth.getRequestHeaders();\n\n // google-auth-library v10 uses a Headers object rather than a plain object\n if ('entries' in headers) {\n return { headers: Object.fromEntries(headers.entries()) };\n } if (_.isObject(headers)) {\n return { headers: headers as Record<string, string> };\n }\n throw new Error('unexpected headers returned from getRequestHeaders');\n }\n\n // API key only access passes through the api key as a query param\n // (note this can only provide read-only access)\n if ('apiKey' in auth && auth.apiKey) {\n return { searchParams: { key: auth.apiKey } };\n }\n\n // RAW ACCESS TOKEN\n if ('token' in auth && auth.token) {\n return { headers: { Authorization: `Bearer ${auth.token}` } };\n }\n\n throw new Error('Invalid auth');\n}\n\n/**\n * Google Sheets document\n *\n * @description\n * **This class represents an entire google spreadsheet document**\n * Provides methods to interact with document metadata/settings, formatting, manage sheets, and acts as the main gateway to interacting with sheets and data that the document contains.q\n *\n */\nexport class GoogleSpreadsheet {\n readonly spreadsheetId: string;\n\n public auth: GoogleApiAuth;\n get authMode() {\n return getAuthMode(this.auth);\n }\n\n private _rawSheets: any;\n private _rawProperties = null as SpreadsheetProperties | null;\n private _spreadsheetUrl = null as string | null;\n private _deleted = false;\n\n /**\n * Sheets API [ky](https://github.com/sindresorhus/ky?tab=readme-ov-file#kycreatedefaultoptions) instance\n * authentication is automatically attached\n * can be used if unsupported sheets calls need to be made\n * @see https://developers.google.com/sheets/api/reference/rest\n * */\n readonly sheetsApi: KyInstance;\n\n /**\n * Drive API [ky](https://github.com/sindresorhus/ky?tab=readme-ov-file#kycreatedefaultoptions) instance\n * authentication automatically attached\n * can be used if unsupported drive calls need to be made\n * @topic permissions\n * @see https://developers.google.com/drive/api/v3/reference\n * */\n readonly driveApi: KyInstance;\n\n\n /**\n * initialize new GoogleSpreadsheet\n * @category Initialization\n * */\n constructor(\n /** id of Google spreadsheet doc */\n spreadsheetId: SpreadsheetId,\n /** authentication to use with Google Sheets API */\n auth: GoogleApiAuth,\n /** Additional options */\n options?: {\n /**\n * customize retry behavior --\n * see the [ky documentation](https://github.com/sindresorhus/ky#retry) for details of the available options and defaults.\n * */\n retryConfig?: RetryOptions | number\n }\n ) {\n const { retryConfig } = options || {};\n this.spreadsheetId = spreadsheetId;\n this.auth = auth;\n\n this._rawSheets = {};\n this._spreadsheetUrl = null;\n\n // create a ky instance with sheet root URL and hooks to handle auth\n this.sheetsApi = ky.create({\n prefix: `${SHEETS_API_BASE_URL}/${spreadsheetId}`,\n timeout: 180_000,\n hooks: {\n beforeRequest: [\n ({ request }) => this._setAuthRequestHook(request),\n ],\n beforeError: [\n ({ error }) => this._errorHook(error),\n ],\n },\n retry: retryConfig,\n });\n this.driveApi = ky.create({\n prefix: `${DRIVE_API_BASE_URL}/${spreadsheetId}`,\n hooks: {\n beforeRequest: [({ request }) => this._setAuthRequestHook(request)],\n beforeError: [({ error }) => this._errorHook(error)],\n },\n retry: retryConfig,\n });\n }\n\n\n // INTERNAL UTILITY FUNCTIONS ////////////////////////////////////////////////////////////////////\n\n /** @internal */\n async _setAuthRequestHook(req: Request): Promise<Request> {\n const authConfig = await getRequestAuthConfig(this.auth);\n if (authConfig.headers) {\n Object.entries(authConfig.headers).forEach(([key, val]) => {\n req.headers.set(key, String(val));\n });\n }\n\n if (authConfig.searchParams) {\n const url = new URL(req.url);\n Object.entries(authConfig.searchParams).forEach(([key, val]) => {\n url.searchParams.set(key, String(val));\n });\n // cannot change the URL with ky, so have to return a new request\n return new Request(url, req);\n }\n\n return req;\n }\n\n /** @internal */\n async _errorHook(error: Error) {\n if (!(error instanceof HTTPError)) return error;\n\n // ky pre-parses the response body into error.data (the response body is already consumed)\n const errorData = typeof error.data === 'string' ? (() => {\n try { return JSON.parse(error.data as string); } catch { return undefined; }\n })() : error.data;\n\n if (errorData?.error) {\n const { code, message } = errorData.error;\n error.message = `Google API error - [${code}] ${message}`;\n return error;\n }\n\n if (error.response?.status === 403) {\n if ('apiKey' in this.auth) {\n 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)');\n }\n }\n return error;\n }\n\n /** @internal */\n async _makeSingleUpdateRequest(requestType: string, requestParams: any) {\n const response = await this.sheetsApi.post(':batchUpdate', {\n json: {\n requests: [{ [requestType]: requestParams }],\n includeSpreadsheetInResponse: true,\n // responseRanges: [string]\n // responseIncludeGridData: true\n },\n });\n const data = await response.json<any>();\n\n this._updateRawProperties(data.updatedSpreadsheet.properties);\n _.each(data.updatedSpreadsheet.sheets, (s: any) => this._updateOrCreateSheet(s));\n // console.log('API RESPONSE', response.data.replies[0][requestType]);\n return data.replies[0][requestType];\n }\n\n // TODO: review these types\n // currently only used in batching cell updates\n /** @internal */\n async _makeBatchUpdateRequest(requests: any[], responseRanges?: string | string[]) {\n // this is used for updating batches of cells\n const response = await this.sheetsApi.post(':batchUpdate', {\n json: {\n requests,\n includeSpreadsheetInResponse: true,\n ...responseRanges && {\n responseIncludeGridData: true,\n ...responseRanges !== '*' && { responseRanges },\n },\n },\n });\n\n const data = await response.json<any>();\n this._updateRawProperties(data.updatedSpreadsheet.properties);\n _.each(data.updatedSpreadsheet.sheets, (s: any) => this._updateOrCreateSheet(s));\n }\n\n /** @internal */\n _ensureInfoLoaded() {\n if (!this._rawProperties) throw new Error('You must call `doc.loadInfo()` before accessing this property');\n }\n\n /** @internal */\n _updateRawProperties(newProperties: SpreadsheetProperties) { this._rawProperties = newProperties; }\n\n /** @internal */\n _updateOrCreateSheet(sheetInfo: { properties: WorksheetProperties, data: any, protectedRanges?: ProtectedRange[] }) {\n const { properties, data, protectedRanges } = sheetInfo;\n const { sheetId } = properties;\n if (!this._rawSheets[sheetId]) {\n this._rawSheets[sheetId] = new GoogleSpreadsheetWorksheet(this, properties, data, protectedRanges);\n } else {\n this._rawSheets[sheetId].updateRawData(properties, data, protectedRanges);\n }\n }\n\n // BASIC PROPS //////////////////////////////////////////////////////////////////////////////\n _getProp(param: keyof SpreadsheetProperties) {\n this._ensureInfoLoaded();\n // ideally ensureInfoLoaded would assert that _rawProperties is in fact loaded\n // but this is not currently possible in TS - see https://github.com/microsoft/TypeScript/issues/49709\n return this._rawProperties![param];\n }\n\n get title(): SpreadsheetProperties['title'] { return this._getProp('title'); }\n get locale(): SpreadsheetProperties['locale'] { return this._getProp('locale'); }\n get timeZone(): SpreadsheetProperties['timeZone'] { return this._getProp('timeZone'); }\n get autoRecalc(): SpreadsheetProperties['autoRecalc'] { return this._getProp('autoRecalc'); }\n get defaultFormat(): SpreadsheetProperties['defaultFormat'] { return this._getProp('defaultFormat'); }\n get spreadsheetTheme(): SpreadsheetProperties['spreadsheetTheme'] { return this._getProp('spreadsheetTheme'); }\n get iterativeCalculationSettings(): SpreadsheetProperties['iterativeCalculationSettings'] { return this._getProp('iterativeCalculationSettings'); }\n\n /**\n * update spreadsheet properties\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets#SpreadsheetProperties\n * */\n async updateProperties(properties: Partial<SpreadsheetProperties>) {\n await this._makeSingleUpdateRequest('updateSpreadsheetProperties', {\n properties,\n fields: getFieldMask(properties),\n });\n }\n\n // BASIC INFO ////////////////////////////////////////////////////////////////////////////////////\n async loadInfo(includeCells = false) {\n const response = await this.sheetsApi.get('', {\n searchParams: {\n ...includeCells && { includeGridData: true },\n },\n });\n const data = await response.json<any>();\n this._spreadsheetUrl = data.spreadsheetUrl;\n this._rawProperties = data.properties;\n data.sheets?.forEach((s: any) => this._updateOrCreateSheet(s));\n }\n\n resetLocalCache() {\n this._rawProperties = null;\n this._rawSheets = {};\n }\n\n // WORKSHEETS ////////////////////////////////////////////////////////////////////////////////////\n get sheetCount() {\n this._ensureInfoLoaded();\n return _.values(this._rawSheets).length;\n }\n\n get sheetsById(): Record<WorksheetId, GoogleSpreadsheetWorksheet> {\n this._ensureInfoLoaded();\n return this._rawSheets;\n }\n\n get sheetsByIndex(): GoogleSpreadsheetWorksheet[] {\n this._ensureInfoLoaded();\n return _.sortBy(this._rawSheets, 'index');\n }\n\n get sheetsByTitle(): Record<string, GoogleSpreadsheetWorksheet> {\n this._ensureInfoLoaded();\n return _.keyBy(this._rawSheets, 'title');\n }\n\n /**\n * Add new worksheet to document\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddSheetRequest\n * */\n async addSheet(\n properties: Partial<\n RecursivePartial<WorksheetProperties>\n & {\n headerValues: string[],\n headerRowIndex: number\n }\n > = {}\n ) {\n const response = await this._makeSingleUpdateRequest('addSheet', {\n properties: _.omit(properties, 'headerValues', 'headerRowIndex'),\n });\n // _makeSingleUpdateRequest already adds the sheet\n const newSheetId = response.properties.sheetId;\n const newSheet = this.sheetsById[newSheetId];\n\n if (properties.headerValues) {\n await newSheet.setHeaderRow(properties.headerValues, properties.headerRowIndex);\n }\n\n return newSheet;\n }\n\n /**\n * delete a worksheet\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DeleteSheetRequest\n * */\n async deleteSheet(sheetId: WorksheetId) {\n await this._makeSingleUpdateRequest('deleteSheet', { sheetId });\n delete this._rawSheets[sheetId];\n }\n\n // NAMED RANGES //////////////////////////////////////////////////////////////////////////////////\n\n /**\n * create a new named range\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddNamedRangeRequest\n */\n async addNamedRange(\n /** name of new named range */\n name: string,\n /** GridRange object describing range */\n range: GridRange,\n /** id for named range (optional) */\n namedRangeId?: string\n ) {\n // TODO: add named range to local cache\n return this._makeSingleUpdateRequest('addNamedRange', {\n namedRange: {\n name,\n namedRangeId,\n range,\n },\n });\n }\n\n /**\n * delete a named range\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DeleteNamedRangeRequest\n * */\n async deleteNamedRange(\n /** id of named range to delete */\n namedRangeId: NamedRangeId\n ) {\n // TODO: remove named range from local cache\n return this._makeSingleUpdateRequest('deleteNamedRange', { namedRangeId });\n }\n\n // LOADING CELLS /////////////////////////////////////////////////////////////////////////////////\n\n /** fetch cell data into local cache */\n async loadCells(\n /**\n * single filter or array of filters\n * strings are treated as A1 ranges, objects are treated as GridRange objects,\n * objects with a `developerMetadataLookup` key are treated as DeveloperMetadataLookup filters\n * pass nothing to fetch all cells\n * */\n filters?: DataFilter | DataFilter[]\n ) {\n // TODO: switch to this mode if using a read-only auth token?\n const readOnlyMode = this.authMode === AUTH_MODES.API_KEY;\n\n const filtersArray = _.isArray(filters) ? filters : [filters];\n const dataFilters = _.map(filtersArray, (filter) => {\n if (_.isString(filter)) {\n return readOnlyMode ? filter : { a1Range: filter };\n }\n if (_.isObject(filter)) {\n if (readOnlyMode) {\n throw new Error('Only A1 ranges are supported when fetching cells with read-only access (using only an API key)');\n }\n if ('developerMetadataLookup' in filter) {\n return { developerMetadataLookup: filter.developerMetadataLookup };\n }\n return { gridRange: filter };\n }\n throw new Error('Each filter must be an A1 range string or a gridrange object');\n });\n\n let result;\n // when using an API key only, we must use the regular get endpoint\n // because :getByDataFilter requires higher access\n if (this.authMode === AUTH_MODES.API_KEY) {\n const params = new URLSearchParams();\n params.append('includeGridData', 'true');\n dataFilters.forEach((singleFilter) => {\n if (!_.isString(singleFilter)) {\n throw new Error('Only A1 ranges are supported when fetching cells with read-only access (using only an API key)');\n }\n params.append('ranges', singleFilter);\n });\n result = await this.sheetsApi.get('', {\n searchParams: params,\n });\n // otherwise we use the getByDataFilter endpoint because it is more flexible\n } else {\n result = await this.sheetsApi.post(':getByDataFilter', {\n json: {\n includeGridData: true,\n dataFilters,\n },\n });\n }\n\n const data = await result?.json<any>();\n _.each(data.sheets, (sheet: any) => { this._updateOrCreateSheet(sheet); });\n }\n\n // EXPORTING /////////////////////////////////////////////////////////////\n\n /**\n * export/download helper, not meant to be called directly (use downloadAsX methods on spreadsheet and worksheet instead)\n * @internal\n */\n async _downloadAs(\n fileType: ExportFileTypes,\n worksheetId: WorksheetId | undefined,\n returnStreamInsteadOfBuffer?: boolean\n ) {\n // see https://stackoverflow.com/questions/11619805/using-the-google-drive-api-to-download-a-spreadsheet-in-csv-format/51235960#51235960\n\n if (!EXPORT_CONFIG[fileType]) throw new Error(`unsupported export fileType - ${fileType}`);\n if (EXPORT_CONFIG[fileType].singleWorksheet) {\n if (worksheetId === undefined) throw new Error(`Must specify worksheetId when exporting as ${fileType}`);\n } else if (worksheetId) throw new Error(`Cannot specify worksheetId when exporting as ${fileType}`);\n\n // google UI shows \"html\" but passes through \"zip\"\n if (fileType === 'html') fileType = 'zip';\n\n if (!this._spreadsheetUrl) throw new Error('Cannot export sheet that is not fully loaded');\n\n const exportUrl = this._spreadsheetUrl.replace('edit', 'export');\n const response = await this.sheetsApi.get(exportUrl, {\n prefix: '', // unset baseUrl since we're not hitting the normal sheets API\n searchParams: {\n id: this.spreadsheetId,\n format: fileType,\n // worksheetId can be 0\n ...worksheetId !== undefined && { gid: worksheetId },\n },\n });\n if (returnStreamInsteadOfBuffer) {\n return response.body;\n }\n return response.arrayBuffer();\n }\n\n /**\n * exports entire document as html file (zipped)\n * @topic export\n * */\n async downloadAsZippedHTML(): Promise<ArrayBuffer>;\n async downloadAsZippedHTML(returnStreamInsteadOfBuffer: false): Promise<ArrayBuffer>;\n async downloadAsZippedHTML(returnStreamInsteadOfBuffer: true): Promise<ReadableStream>;\n async downloadAsZippedHTML(returnStreamInsteadOfBuffer?: boolean) {\n return this._downloadAs('html', undefined, returnStreamInsteadOfBuffer);\n }\n\n /**\n * @deprecated\n * use `doc.downloadAsZippedHTML()` instead\n * */\n async downloadAsHTML(returnStreamInsteadOfBuffer?: boolean) {\n return this._downloadAs('html', undefined, returnStreamInsteadOfBuffer);\n }\n\n /**\n * exports entire document as xlsx spreadsheet (Microsoft Office Excel)\n * @topic export\n * */\n async downloadAsXLSX(): Promise<ArrayBuffer>;\n async downloadAsXLSX(returnStreamInsteadOfBuffer: false): Promise<ArrayBuffer>;\n async downloadAsXLSX(returnStreamInsteadOfBuffer: true): Promise<ReadableStream>;\n async downloadAsXLSX(returnStreamInsteadOfBuffer = false) {\n return this._downloadAs('xlsx', undefined, returnStreamInsteadOfBuffer);\n }\n /**\n * exports entire document as ods spreadsheet (Open Office)\n * @topic export\n */\n async downloadAsODS(): Promise<ArrayBuffer>;\n async downloadAsODS(returnStreamInsteadOfBuffer: false): Promise<ArrayBuffer>;\n async downloadAsODS(returnStreamInsteadOfBuffer: true): Promise<ReadableStream>;\n async downloadAsODS(returnStreamInsteadOfBuffer = false) {\n return this._downloadAs('ods', undefined, returnStreamInsteadOfBuffer);\n }\n\n\n async delete() {\n await this.driveApi.delete('');\n this._deleted = true;\n // endpoint returns nothing when successful\n }\n\n // PERMISSIONS ///////////////////////////////////////////////////////////////////////////////////\n\n /**\n * list all permissions entries for doc\n */\n async listPermissions(): Promise<PermissionsList> {\n const listReq = await this.driveApi.get('permissions', {\n searchParams: {\n fields: 'permissions(id,type,emailAddress,domain,role,displayName,photoLink,deleted)',\n },\n });\n const data = await listReq.json<{ permissions: PermissionsList }>();\n return data.permissions;\n }\n\n async setPublicAccessLevel(role: PublicPermissionRoles | false) {\n const permissions = await this.listPermissions();\n const existingPublicPermission = _.find(permissions, (p) => p.type === 'anyone');\n\n if (role === false) {\n if (!existingPublicPermission) {\n // doc is already not public... could throw an error or just do nothing\n return;\n }\n await this.driveApi.delete(`permissions/${existingPublicPermission.id}`);\n } else {\n const _shareReq = await this.driveApi.post('permissions', {\n json: {\n role: role || 'viewer',\n type: 'anyone',\n },\n });\n }\n }\n\n /** share document to email or domain */\n async share(emailAddressOrDomain: string, opts?: {\n /** set role level, defaults to owner */\n role?: PermissionRoles,\n\n /** set to true if email is for a group */\n isGroup?: boolean,\n\n /** set to string to include a custom message, set to false to skip sending a notification altogether */\n emailMessage?: string | false,\n\n // moveToNewOwnersRoot?: string,\n // /** send a notification email (default = true) */\n // sendNotificationEmail?: boolean,\n // /** support My Drives and shared drives (default = false) */\n // supportsAllDrives?: boolean,\n\n // /** Issue the request as a domain administrator */\n // useDomainAdminAccess?: boolean,\n }) {\n let emailAddress: string | undefined;\n let domain: string | undefined;\n if (emailAddressOrDomain.includes('@')) {\n emailAddress = emailAddressOrDomain;\n } else {\n domain = emailAddressOrDomain;\n }\n\n\n const shareReq = await this.driveApi.post('permissions', {\n searchParams: {\n ...opts?.emailMessage === false && { sendNotificationEmail: false },\n ..._.isString(opts?.emailMessage) && { emailMessage: opts?.emailMessage },\n ...opts?.role === 'owner' && { transferOwnership: true },\n },\n json: {\n role: opts?.role || 'writer',\n ...emailAddress && {\n type: opts?.isGroup ? 'group' : 'user',\n emailAddress,\n },\n ...domain && {\n type: 'domain',\n domain,\n },\n },\n });\n\n return shareReq.json();\n }\n\n /**\n * delete a permission by its ID\n * @see https://developers.google.com/drive/api/v3/reference/permissions/delete\n */\n async deletePermission(permissionId: string) {\n await this.driveApi.delete(`permissions/${permissionId}`);\n }\n\n // DEVELOPER METADATA ///////////////////////////////////////////////////////////////////////////\n\n /**\n * search for developer metadata entries matching the given filters\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.developerMetadata/search\n */\n async searchDeveloperMetadata(\n /** array of DataFilter objects to match against */\n filters: DataFilterObject[]\n ): Promise<DeveloperMetadata[]> {\n const response = await this.sheetsApi.post('developerMetadata:search', {\n json: { dataFilters: filters },\n });\n const data = await response.json<any>();\n if (!data.matchedDeveloperMetadata) return [];\n return data.matchedDeveloperMetadata.map((m: any) => m.developerMetadata);\n }\n\n //\n // CREATE NEW DOC ////////////////////////////////////////////////////////////////////////////////\n static async createNewSpreadsheetDocument(auth: GoogleApiAuth, properties?: Partial<SpreadsheetProperties>) {\n // see updateProperties for more info about available properties\n\n if (getAuthMode(auth) === AUTH_MODES.API_KEY) {\n throw new Error('Cannot use api key only to create a new spreadsheet - it is only usable for read-only access of public docs');\n }\n\n // TODO: handle injecting default credentials if running on google infra\n\n const authConfig = await getRequestAuthConfig(auth);\n\n const response = await ky.post(SHEETS_API_BASE_URL, {\n ...authConfig, // has the auth header\n json: {\n properties,\n },\n });\n\n const data = await response.json<any>();\n const newSpreadsheet = new GoogleSpreadsheet(data.spreadsheetId, auth);\n\n // TODO ideally these things aren't public, might want to refactor anyway\n newSpreadsheet._spreadsheetUrl = data.spreadsheetUrl;\n newSpreadsheet._rawProperties = data.properties;\n _.each(data.sheets, (s: any) => newSpreadsheet._updateOrCreateSheet(s));\n\n return newSpreadsheet;\n }\n}\n"],"mappings":"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"}