/** * Excel (los libros de Microsoft 365) por Microsoft Graph — la fuente única de * sus operaciones para el api, hw-nodes, el dashboard y el servidor MCP. * * ## Por qué un nodo aparte (2026-10-01) * * Ariel pidió las integraciones de Microsoft 365 SEPARADAS, un nodo por * producto, cada uno con su action, su trigger y su AI toolkit, y una app de * Entra por servicio («HostWebhook Excel»): Microsoft sólo deja revocar la app * ENTERA, así que quitarle los ficheros a HostWebhook no le quita los libros, * ni al revés. Éste es el cuarto, después de Outlook, Outlook Calendar y * OneDrive. Diseño: `plans/microsoft-excel.md`. * * ## Diecisiete operaciones en cuatro familias * * - **Rows** — añadir, actualizar, añadir-o-actualizar, leer, buscar y * borrar filas, en una TABLA de Excel o en una hoja con fila de cabeceras * (las columnas se eligen de la lista, como el Google Sheets Action). * - **Tables** — listar, crear y quitar tablas. * - **Sheets & ranges** — listar, añadir y borrar hojas; leer, escribir y * limpiar rangos. * - **Workbooks** — buscar libros y crear uno nuevo. * * ## Se elige como un fichero de OneDrive * * El libro es un `onedriveFile` y la carpeta un `onedriveFolder`: las MISMAS * cadenas que OneDrive (`onedrive-operations.ts`), con el mismo selector y el * mismo «escribir a mano» (un ID, una ruta que empieza por `/` o una * plantilla). No hay un tipo `excelWorkbook` propio. Lo único que Excel añade * al selector es `extensiones` ({@link EXCEL_EXTENSIONES}). * * El nombre del libro elegido se guarda en `operationConfig.workbookName`: un * SATÉLITE que escribe el selector para pintar lo guardado sin preguntar a * Microsoft. No es un parámetro (no sale en las fichas) y quien guarde la * configuración lo tiene que conservar, como `audienceName` de Mailchimp. * * ## Personal y trabajo (lo más importante) * * Microsoft documenta que la API de libros (`/workbook/…`) NO funciona con * ficheros de un OneDrive PERSONAL (outlook.com, hotmail.com): contesta 401 * `FileOpenUserUnauthorized`. Por eso el nodo tiene dos motores: con cuentas * de trabajo o escuela, la API de Excel (todo); con personales, el fichero se * baja y se LEE en memoria, y escribir se niega con un mensaje claro sin * llamar a Microsoft. Las rutas de abajo son las del motor de la API; buscar * y crear libros usan la de Drive, que vale con las dos. */ export declare const EXCEL_OPERATIONS: readonly ["appendRow", "updateRow", "appendOrUpdateRow", "getRows", "lookupRows", "deleteRows", "listTables", "createTable", "deleteTable", "listWorksheets", "addWorksheet", "deleteWorksheet", "readRange", "writeRange", "clearRange", "findWorkbooks", "createWorkbook"]; export type ExcelOperation = (typeof EXCEL_OPERATIONS)[number]; /** Type guard — for DTOs and AI tool calls, where the input is untrusted. */ export declare function isExcelOperation(value: unknown): value is ExcelOperation; /** * El tipo de credencial: el OAuth de la app «HostWebhook Excel» de Entra, con * `Files.ReadWrite` y nada más. Un tipo por servicio de Microsoft, porque cada * uno es su propia app. */ export declare const EXCEL_CREDENTIAL_TYPE = "microsoft_excel_oauth2"; /** «Get rows» / «Find rows» sin «How many». */ export declare const EXCEL_FILAS_POR_DEFECTO = 100; /** Lo más que devuelve una lectura de filas. */ export declare const EXCEL_FILAS_MAXIMAS = 1000; /** * Filas por escritura: las de «Rows (JSON)» en «Append row» y «Create * workbook», y las COINCIDENCIAS de «Update every match» / «Delete every * match», contadas ANTES de escribir — con más, no se escribe nada. */ export declare const EXCEL_FILAS_POR_ESCRITURA = 500; /** * Más llamadas de escritura que éstas en una operación (bloques de filas * contiguas al actualizar o borrar) → se abre una sesión persistente del libro * (`createSession`). Sin sesión, Microsoft lo da por bueno sólo para llamadas * sueltas. */ export declare const EXCEL_LLAMADAS_SIN_SESION = 5; /** Filas que se recorren buscando (Find / Update / Delete rows). */ export declare const EXCEL_FILAS_DE_BUSQUEDA = 50000; /** Celdas de «Read range», «Write range» y «Clear range». */ export declare const EXCEL_CELDAS_MAXIMAS = 10000; /** «Find workbooks»: los que devuelve si no se dice, y el máximo. */ export declare const EXCEL_LIBROS_POR_DEFECTO = 25; export declare const EXCEL_LIBROS_MAXIMOS = 100; /** * Las extensiones que lista el selector y abre el nodo: las de Office Open XML * (`.xls` no: la API de Excel no lo abre). `.xlsm` lleva macros; el lector las * ignora. Las mismas que pasa OneDrive en su selector de ficheros. */ export declare const EXCEL_EXTENSIONES: readonly ["xlsx", "xlsm"]; /** * Lo más que se BAJA de un libro para leerlo en memoria (cuentas personales y * el trigger con cualquier cuenta): se mira el `size` antes de bajar nada, y * el cuerpo se corta en cuanto pasa de aquí aunque `size` mienta. */ export declare const EXCEL_FICHERO_MAXIMO: number; /** Lo más que se infla UNA entrada del ZIP de un libro (una hoja, en XML). */ export declare const EXCEL_XML_DE_HOJA_MAXIMO: number; /** Lo más que se infla sumando las entradas leídas en UNA lectura. */ export declare const EXCEL_ZIP_LEIDO_MAXIMO: number; /** «Find rows»: la celda entera, o que la contenga. Vacío = `equals`. */ export declare const EXCEL_MODOS_DE_BUSQUEDA: readonly ["equals", "contains"]; /** «Clear range»: `applyTo` de Graph (`Contents`, `Formats`, `All`). Vacío = `contents`. */ export declare const EXCEL_LIMPIAR: readonly ["contents", "formats", "all"]; /** «Read range»: `values`, `text` (como se ve) o `formulas`. Vacío = `values`. */ export declare const EXCEL_LEER_COMO: readonly ["values", "text", "formulas"]; /** * «Create workbook», si el fichero ya está. Vacío = `fail`. Nunca se reemplaza * nada que no se pidió: `fail` y `rename` comprueban antes y suben por sesión * con `conflictBehavior: fail`; sólo `replace` reemplaza. */ export declare const EXCEL_SI_EXISTE: readonly ["fail", "rename", "replace"]; export interface ExcelParamSpec { /** Field key — also the property name on operationConfig. */ name: string; label: string; /** * Param type, so the dashboard renders the right control. * - `onedriveFile` / `onedriveFolder` — los selectores de OneDrive, tal * cual (las mismas cadenas que `OnedriveParamSpec`); * - `excelWorksheet` / `excelTable` — una hoja o una tabla del libro que * está en `workbookId` de la misma operación; * - `excelColumn` — una cabecera de la hoja o tabla elegida; * - `excelColumns` — el editor de columnas: `[{ column, value }]`, cada * valor con plantillas (la forma de la pantalla; el modelo manda lo * mismo como objeto en `rowValues`, ver `excel-toolkit.ts`). * Las dependencias no se declaran aquí, como `jiraTransition`: la página * lee `operationConfig` y se las pasa al selector. */ type: 'string' | 'text' | 'number' | 'boolean' | 'select' | 'json' | 'onedriveFile' | 'onedriveFolder' | 'excelWorksheet' | 'excelTable' | 'excelColumn' | 'excelColumns'; required?: boolean; description: string; /** * El hueco vacío. En un `select` opcional dice lo que pasará si se deja sin * elegir («Equals (default)»): lo enseña, no lo marca — ningún campo nace * elegido. */ placeholder?: string; /** For `select`. */ options?: readonly string[]; /** For `number`. */ min?: number; max?: number; /** Sólo en `workbookId` ({@link EXCEL_EXTENSIONES}): lo que lista el selector de OneDrive. */ extensiones?: readonly string[]; } export interface ExcelOperationSpec { label: string; description: string; /** The Graph route this maps to, so the mapping is checkable. */ apiRoute: string; /** Changes something in OneDrive (as opposed to only reading it). */ writes?: boolean; params: ExcelParamSpec[]; } export declare const EXCEL_OPERATION_SPECS: Record;