import ExcelJS from "exceljs"; import path from "node:path"; import { register } from "../engine/registry"; import { RuntimeError, type CapabilityHandler } from "../engine/types"; import { acquireWorkbook, releaseWorkbook } from "./excel-store"; type WorkbookSource = { workbook: ExcelJS.Workbook; filePath: string }; type CopiedRange = { values: unknown[][]; styles: Partial[][] }; let copiedRange: CopiedRange | undefined; function value(args: Record, keys: string[], fallback: unknown = ""): unknown { for (const key of keys) { const candidate = args[key]; if (candidate !== undefined && candidate !== null && candidate !== "") return candidate; } return fallback; } function text(args: Record, keys: string[], fallback = ""): string { return String(value(args, keys, fallback)); } function truthy(candidate: unknown, fallback = false): boolean { if (candidate === undefined || candidate === null || candidate === "") return fallback; return [true, 1, "1", "true", "True", "TRUE", "yes", "是", "开启"].includes(candidate as never); } function numberValue(candidate: unknown, fallback: number): number { const result = Number(candidate); return Number.isFinite(result) ? result : fallback; } function requiredPath(candidate: unknown, nodeId: string, componentId: string): string { const raw = typeof candidate === "object" && candidate !== null ? (candidate as { path?: unknown }).path : candidate; const filePath = String(raw ?? "").trim(); if (!filePath) { throw new RuntimeError("validation_error", "Excel path is empty", { nodeId, componentId, recommendation: "Configure an Excel workbook path" }); } return path.resolve(filePath); } async function loadWorkbook(args: Record, nodeId: string, componentId: string): Promise { const filePath = requiredPath(value(args, ["workbook", "objExcel", "objExcelWorkBook", "file", "path"]), nodeId, componentId); const workbook = await acquireWorkbook(filePath); return { workbook, filePath }; } function worksheetOf(workbook: ExcelJS.Workbook, args: Record): ExcelJS.Worksheet { const name = text(args, ["sheet", "sheetName"]); const sheet = name ? workbook.getWorksheet(name) : workbook.worksheets[0]; if (!sheet) throw new Error(`Sheet not found: ${name || "first sheet"}`); return sheet; } async function saveWhenRequested(source: WorkbookSource, args: Record, fallback = false): Promise { if (truthy(value(args, ["save", "isSave"]), fallback)) await source.workbook.xlsx.writeFile(source.filePath); } function columnNumber(candidate: unknown): number { const raw = String(candidate ?? "A").trim().toUpperCase(); if (/^\d+$/.test(raw)) return Math.max(1, Number(raw)); if (!/^[A-Z]+$/.test(raw)) throw new Error(`Invalid column: ${raw}`); let result = 0; for (const character of raw) result = result * 26 + character.charCodeAt(0) - 64; return result; } function columnLetter(index: number): string { let current = Math.max(1, Math.trunc(index)); let result = ""; while (current > 0) { current -= 1; result = String.fromCharCode(65 + current % 26) + result; current = Math.floor(current / 26); } return result; } function cellValue(candidate: ExcelJS.CellValue): unknown { if (candidate && typeof candidate === "object" && "result" in candidate) return candidate.result ?? ""; if (candidate && typeof candidate === "object" && "text" in candidate) return candidate.text; return candidate ?? ""; } function listValue(candidate: unknown): unknown[] { if (Array.isArray(candidate)) return candidate; const raw = String(candidate ?? "").trim(); if (!raw) return []; try { const parsed = JSON.parse(raw); return Array.isArray(parsed) ? parsed : [parsed]; } catch { try { const parsed = JSON.parse(raw.replace(/'/g, '"')); return Array.isArray(parsed) ? parsed : [parsed]; } catch { return raw.split(",").map((item) => item.trim()); } } } function tableValue(candidate: unknown): unknown[][] { return listValue(candidate).map((item) => Array.isArray(item) ? item : [item]); } function rangeBounds(sheet: ExcelJS.Worksheet, range: string): { top: number; left: number; bottom: number; right: number } { const normalized = range.trim() || `A1:${columnLetter(Math.max(1, sheet.columnCount))}${Math.max(1, sheet.rowCount)}`; const [start, end = start] = normalized.split(":").map((part) => part.trim()); const first = sheet.getCell(start); const last = sheet.getCell(end); return { top: Math.min(Number(first.row), Number(last.row)), left: Math.min(Number(first.col), Number(last.col)), bottom: Math.max(Number(first.row), Number(last.row)), right: Math.max(Number(first.col), Number(last.col)) }; } function explicitRange(args: Record, sheet: ExcelJS.Worksheet): string { const direct = text(args, ["excelRng", "range", "cellPosRange", "cellPos", "sortRange"]); if (direct) return direct; const top = numberValue(value(args, ["startRow", "row"]), 1); const left = columnNumber(value(args, ["startColumn", "column"])); const bottom = numberValue(value(args, ["lastRow", "endRow"]), top); const right = columnNumber(value(args, ["lastColumn", "endColumn"], left)); return `${columnLetter(left)}${top}:${columnLetter(right)}${bottom || Math.max(1, sheet.rowCount)}`; } function cellsInRange(sheet: ExcelJS.Worksheet, range: string): ExcelJS.Cell[] { const bounds = rangeBounds(sheet, range); const cells: ExcelJS.Cell[] = []; for (let row = bounds.top; row <= bounds.bottom; row += 1) { for (let column = bounds.left; column <= bounds.right; column += 1) cells.push(sheet.getCell(row, column)); } return cells; } function cloneStyle(style: Partial): Partial { return JSON.parse(JSON.stringify(style)) as Partial; } function colorArgb(candidate: unknown): string { if (Array.isArray(candidate)) { const [red = 0, green = 0, blue = 0] = candidate.map((item) => Math.max(0, Math.min(255, Number(item)))); return `FF${red.toString(16).padStart(2, "0")}${green.toString(16).padStart(2, "0")}${blue.toString(16).padStart(2, "0")}`.toUpperCase(); } const raw = String(candidate ?? "#000000").trim(); if (raw.startsWith("[")) { try { return colorArgb(JSON.parse(raw)); } catch { return "FF000000"; } } const hex = raw.replace(/^#/, "").toUpperCase(); if (/^[0-9A-F]{8}$/.test(hex)) return hex; return `FF${/^[0-9A-F]{6}$/.test(hex) ? hex : "000000"}`; } function comparison(left: unknown, right: unknown): number { if (typeof left === "number" && typeof right === "number") return left - right; return String(left ?? "").localeCompare(String(right ?? ""), "zh-CN", { numeric: true, sensitivity: "base" }); } function registerMany(ids: string[], handler: CapabilityHandler): void { register(ids, handler); } const writeRow: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); const start = text(args, ["cellPos"]); const row = start ? Number(sheet.getCell(start).row) : numberValue(value(args, ["row", "rowIndex"]), 1); const column = start ? Number(sheet.getCell(start).col) : columnNumber(value(args, ["column"])); listValue(value(args, ["contents", "value"])).forEach((item, index) => { sheet.getCell(row, column + index).value = item as ExcelJS.CellValue; }); await saveWhenRequested(source, args); return `${columnLetter(column)}${row}`; }; const writeColumn: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); const start = text(args, ["cellPos"]); const row = start ? Number(sheet.getCell(start).row) : numberValue(value(args, ["row"]), 1); const column = start ? Number(sheet.getCell(start).col) : columnNumber(value(args, ["column"])); listValue(value(args, ["contents", "value"])).forEach((item, index) => { sheet.getCell(row + index, column).value = item as ExcelJS.CellValue; }); await saveWhenRequested(source, args); return `${columnLetter(column)}${row}`; }; const writeRange: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); const row = numberValue(value(args, ["row", "startRow"]), 1); const column = columnNumber(value(args, ["column", "startColumn"])); tableValue(value(args, ["contents", "value"])).forEach((items, rowOffset) => { items.forEach((item, columnOffset) => { sheet.getCell(row + rowOffset, column + columnOffset).value = item as ExcelJS.CellValue; }); }); await saveWhenRequested(source, args); return `${columnLetter(column)}${row}`; }; const readRow: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); const start = text(args, ["cellPos"]); const row = start ? Number(sheet.getCell(start).row) : numberValue(value(args, ["row", "rowIndex"]), 1); const firstColumn = start ? Number(sheet.getCell(start).col) : 1; const result: unknown[] = []; for (let column = firstColumn; column <= Math.max(firstColumn, sheet.columnCount); column += 1) result.push(cellValue(sheet.getCell(row, column).value)); return result; }; const readColumn: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); const start = text(args, ["cellPos"]); const row = start ? Number(sheet.getCell(start).row) : 1; const column = start ? Number(sheet.getCell(start).col) : columnNumber(value(args, ["column"])); const result: unknown[] = []; for (let current = row; current <= Math.max(row, sheet.rowCount); current += 1) result.push(cellValue(sheet.getCell(current, column).value)); return result; }; const readRange: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); const bounds = rangeBounds(sheet, explicitRange(args, sheet)); const result: unknown[][] = []; for (let row = bounds.top; row <= bounds.bottom; row += 1) { const values: unknown[] = []; for (let column = bounds.left; column <= bounds.right; column += 1) values.push(cellValue(sheet.getCell(row, column).value)); result.push(values); } return result; }; const readColumnCount: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); const row = numberValue(value(args, ["row"]), 1); let last = 0; for (let column = 1; column <= sheet.columnCount; column += 1) { if (cellValue(sheet.getCell(row, column).value) !== "") last = column; } return last; }; const findOrReplace: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); const needle = text(args, ["what", "input"]); const replacement = text(args, ["replacement", "replace"]); const mode = text(args, ["mode"], "0"); const replaceMode = mode === "1" || mode.includes("替换") || replacement !== ""; const exact = truthy(value(args, ["wholeWord"])) || text(args, ["lookAt"]) === "1"; const matchCase = truthy(value(args, ["matchCase"])); const range = text(args, ["range"]); const cells = cellsInRange(sheet, range || `A1:${columnLetter(Math.max(1, sheet.columnCount))}${Math.max(1, sheet.rowCount)}`); const matches: string[] = []; for (const cell of cells) { const original = String(cellValue(cell.value)); const haystack = matchCase ? original : original.toLowerCase(); const target = matchCase ? needle : needle.toLowerCase(); const matched = exact ? haystack === target : haystack.includes(target); if (!matched) continue; matches.push(cell.address); if (replaceMode) { const escaped = needle.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); cell.value = exact ? replacement : original.replace(new RegExp(escaped, matchCase ? "g" : "gi"), replacement); } if (truthy(value(args, ["matchFirst"]))) break; } await saveWhenRequested(source, args); return matches; }; const sortRange: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); const range = text(args, ["sortRange", "range"]) || `A1:${columnLetter(Math.max(1, sheet.columnCount))}${Math.max(1, sheet.rowCount)}`; const bounds = rangeBounds(sheet, range); const hasHeader = truthy(value(args, ["header"])); const dataStart = bounds.top + (hasHeader ? 1 : 0); const rows: unknown[][] = []; for (let row = dataStart; row <= bounds.bottom; row += 1) { const values: unknown[] = []; for (let column = bounds.left; column <= bounds.right; column += 1) values.push(cellValue(sheet.getCell(row, column).value)); rows.push(values); } let sortOffset = 0; const rule = value(args, ["rule"]); if (rule) { try { const parsed = typeof rule === "string" ? JSON.parse(rule) as Record : rule as Record; sortOffset = Math.max(0, columnNumber(value(parsed, ["column", "key"], bounds.left)) - bounds.left); } catch { /* use first column */ } } const descending = ["2", "-1", "desc", "descending", "降序"].includes(text(args, ["sortOrder"]).toLowerCase()); rows.sort((left, right) => comparison(left[sortOffset], right[sortOffset]) * (descending ? -1 : 1)); rows.forEach((values, rowOffset) => values.forEach((item, columnOffset) => { sheet.getCell(dataStart + rowOffset, bounds.left + columnOffset).value = item as ExcelJS.CellValue; })); await saveWhenRequested(source, args); return rows; }; function criterionMatches(candidate: unknown, criterion: unknown, condition: string): boolean { const left = String(candidate ?? ""); const right = String(criterion ?? ""); if (["contains", "包含", "1"].includes(condition)) return left.includes(right); if (["notContains", "不包含", "2"].includes(condition)) return !left.includes(right); if (["greater", ">", "3"].includes(condition)) return Number(candidate) > Number(criterion); if (["less", "<", "4"].includes(condition)) return Number(candidate) < Number(criterion); if (["notEqual", "!=", "5"].includes(condition)) return left !== right; return left === right; } const filterRows: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); let column = columnNumber(value(args, ["column"], "A")); let criterion = value(args, ["criteria"], ""); let condition = text(args, ["condition"], "equal"); const rule = value(args, ["rule"]); if (rule) { try { const parsed = typeof rule === "string" ? JSON.parse(rule) as Record : rule as Record; column = columnNumber(value(parsed, ["column"], column)); criterion = value(parsed, ["criteria", "value"], criterion); condition = text(parsed, ["condition", "operator"], condition); } catch { /* use direct fields */ } } const startRow = numberValue(value(args, ["startRow"]), 1); const endRowRaw = numberValue(value(args, ["endRow"]), sheet.rowCount); const endRow = endRowRaw < 0 ? sheet.rowCount : endRowRaw; const headerRow = truthy(value(args, ["onHeader"])) ? startRow : 0; for (let row = startRow; row <= endRow; row += 1) { if (row === headerRow) continue; sheet.getRow(row).hidden = !criterionMatches(cellValue(sheet.getCell(row, column).value), criterion, condition); } sheet.autoFilter = { from: { row: startRow, column: 1 }, to: { row: endRow, column: Math.max(1, sheet.columnCount) } }; await saveWhenRequested(source, args); return { column: columnLetter(column), criterion, condition }; }; const clearFilter: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); for (let row = 1; row <= sheet.rowCount; row += 1) sheet.getRow(row).hidden = false; sheet.autoFilter = undefined; await saveWhenRequested(source, args); return true; }; const deleteDuplicates: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); const columns = listValue(value(args, ["columns"], "A")).map(columnNumber); const hasHeader = truthy(value(args, ["header"])); const seen = new Set(); const duplicates: number[] = []; for (let row = hasHeader ? 2 : 1; row <= sheet.rowCount; row += 1) { const key = JSON.stringify(columns.map((column) => cellValue(sheet.getCell(row, column).value))); if (seen.has(key)) duplicates.push(row); else seen.add(key); } duplicates.reverse().forEach((row) => sheet.spliceRows(row, 1)); await saveWhenRequested(source, args); return duplicates.length; }; function copyCells(sheet: ExcelJS.Worksheet, range: string): CopiedRange { const bounds = rangeBounds(sheet, range); const values: unknown[][] = []; const styles: Partial[][] = []; for (let row = bounds.top; row <= bounds.bottom; row += 1) { const valueRow: unknown[] = []; const styleRow: Partial[] = []; for (let column = bounds.left; column <= bounds.right; column += 1) { const cell = sheet.getCell(row, column); valueRow.push(cell.value); styleRow.push(cloneStyle(cell.style)); } values.push(valueRow); styles.push(styleRow); } return { values, styles }; } const copyRange: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); const range = explicitRange(args, sheet); copiedRange = copyCells(sheet, range); const destinationColumn = value(args, ["startColumn2"]); const destinationRow = value(args, ["startRow2"]); if (destinationColumn !== "" && destinationRow !== "") { const startColumn = columnNumber(destinationColumn); const startRow = numberValue(destinationRow, 1); copiedRange.values.forEach((items, rowOffset) => items.forEach((item, columnOffset) => { const cell = sheet.getCell(startRow + rowOffset, startColumn + columnOffset); cell.value = item as ExcelJS.CellValue; cell.style = cloneStyle(copiedRange!.styles[rowOffset][columnOffset]); })); await saveWhenRequested(source, args); } return copiedRange.values; }; const pasteRange: CapabilityHandler = async (_ctx, node, args) => { if (!copiedRange) throw new RuntimeError("validation_error", "No copied Excel range is available", { nodeId: node.id, componentId: node.componentId }); const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); const start = text(args, ["Start"]); const row = start ? Number(sheet.getCell(start).row) : numberValue(value(args, ["row"]), 1); const column = start ? Number(sheet.getCell(start).col) : columnNumber(value(args, ["column"])); const transpose = truthy(value(args, ["transpose"])); const values = transpose ? copiedRange.values[0].map((_, index) => copiedRange!.values.map((items) => items[index])) : copiedRange.values; values.forEach((items, rowOffset) => items.forEach((item, columnOffset) => { const cell = sheet.getCell(row + rowOffset, column + columnOffset); cell.value = item as ExcelJS.CellValue; if (!transpose) cell.style = cloneStyle(copiedRange!.styles[rowOffset][columnOffset]); })); await saveWhenRequested(source, args); return `${columnLetter(column)}${row}`; }; const createSheet: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const name = text(args, ["sheet", "sheetName", "name"], `Sheet${source.workbook.worksheets.length + 1}`); if (source.workbook.getWorksheet(name)) throw new Error(`Sheet already exists: ${name}`); source.workbook.addWorksheet(name); await saveWhenRequested(source, args); return name; }; const deleteSheet: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); if (source.workbook.worksheets.length <= 1) throw new Error("Cannot delete the only worksheet"); source.workbook.removeWorksheet(sheet.id); await saveWhenRequested(source, args); return sheet.name; }; const activateSheet: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const mode = text(args, ["mode"]); const index = mode.includes("序号") ? numberValue(value(args, ["index"]), 1) : 0; const sheet = index ? source.workbook.worksheets[index - 1] : worksheetOf(source.workbook, args); if (!sheet) throw new Error(`Sheet not found: ${index}`); source.workbook.views = [{ activeTab: source.workbook.worksheets.indexOf(sheet), firstSheet: 0, visibility: "visible", x: 0, y: 0, width: 12000, height: 8000 }]; await saveWhenRequested(source, args); return sheet.name; }; const getSheetNames: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); return source.workbook.worksheets.map((sheet) => sheet.name); }; function setFontColor(cell: ExcelJS.Cell, argb: string): void { cell.font = { ...(cell.font || {}), color: { argb } }; } function setBackground(cell: ExcelJS.Cell, argb: string): void { cell.fill = { type: "pattern", pattern: "solid", fgColor: { argb } }; } const setColor = (kind: "font" | "background", rangeMode: boolean): CapabilityHandler => async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); const range = rangeMode ? explicitRange(args, sheet) : text(args, ["cellPos"]) || `${text(args, ["column"], "A")}${numberValue(value(args, ["row"]), 1)}`; const argb = colorArgb(value(args, ["color", "cellColor"])); for (const cell of cellsInRange(sheet, range)) { if (kind === "font") setFontColor(cell, argb); else setBackground(cell, argb); } await saveWhenRequested(source, args); return argb; }; const readBackground: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); const cell = sheet.getCell(numberValue(value(args, ["row"]), 1), columnNumber(value(args, ["column"]))); const fill = cell.fill as ExcelJS.FillPattern | undefined; return fill?.type === "pattern" ? fill.fgColor?.argb ?? "" : ""; }; const autoFill: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); const sourceRange = text(args, ["sourceRange", "excelRng"]); const destination = text(args, ["destination"]); const sourceBounds = rangeBounds(sheet, sourceRange); const destinationBounds = rangeBounds(sheet, destination); const sourceValues = copyCells(sheet, sourceRange).values; const vertical = sourceBounds.left === sourceBounds.right; for (let row = destinationBounds.top; row <= destinationBounds.bottom; row += 1) { for (let column = destinationBounds.left; column <= destinationBounds.right; column += 1) { const sourceRow = (row - destinationBounds.top) % sourceValues.length; const sourceColumn = (column - destinationBounds.left) % sourceValues[0].length; let next = sourceValues[sourceRow][sourceColumn]; const sequence = vertical ? sourceValues.map((items) => items[0]) : sourceValues[0]; const offset = vertical ? row - sourceBounds.top : column - sourceBounds.left; if (sequence.length >= 2 && sequence.every((item) => typeof item === "number")) { next = Number(sequence[0]) + (Number(sequence[1]) - Number(sequence[0])) * offset; } sheet.getCell(row, column).value = next as ExcelJS.CellValue; } } await saveWhenRequested(source, args); return destination; }; const splitColumn: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); const sourceColumn = columnNumber(value(args, ["column"])); const destination = text(args, ["destination"]); const destinationColumn = destination ? Number(sheet.getCell(destination).col) : sourceColumn; const startRow = numberValue(value(args, ["startRow"]), 1); const endRow = numberValue(value(args, ["endRow"]), sheet.rowCount); const delimiters: string[] = []; if (truthy(value(args, ["tab"]), true)) delimiters.push("\\t"); if (truthy(value(args, ["semicolon"]))) delimiters.push(";"); if (truthy(value(args, ["comma"]))) delimiters.push(","); if (truthy(value(args, ["space"]))) delimiters.push("\\s+"); if (truthy(value(args, ["other"]))) delimiters.push(text(args, ["otherChar"]).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")); const separator = new RegExp(delimiters.filter(Boolean).join("|") || "\\t"); for (let row = startRow; row <= endRow; row += 1) { String(cellValue(sheet.getCell(row, sourceColumn).value)).split(separator).forEach((item, offset) => { sheet.getCell(row, destinationColumn + offset).value = item; }); } await saveWhenRequested(source, args); return `${columnLetter(destinationColumn)}${startRow}`; }; const closeWorkbook: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); if (truthy(value(args, ["save", "isSave"]), true)) await source.workbook.xlsx.writeFile(source.filePath); releaseWorkbook(source.filePath); return source.filePath; }; const deleteRange: CapabilityHandler = async (_ctx, node, args) => { const source = await loadWorkbook(args, node.id, node.componentId); const sheet = worksheetOf(source.workbook, args); const range = explicitRange(args, sheet); for (const cell of cellsInRange(sheet, range)) cell.value = null; await saveWhenRequested(source, args); return range; }; export const EXCEL_COMMON_IDS = [ "Excel_WriteColumn", "Excel_WriteColumnNew", "Excel_Cs_WriteColumn", "Excel_WriteRow", "Excel_WriteRowNew", "Excel_Cs_WriteRow", "Excel_Cs_WriteRowV2", "Excel_Cs_WriteRangeV2", "Excel_GetRowData", "Excel_GetRowDataNew", "Excel_Cs_ReadRow", "Excel_GetColData", "Excel_Cs_ReadColumnCount", "Excel_Cs_ReadRange", "Excel_Cs_Find", "Excel_Cs_FindV2", "Excel_Cs_Search", "Excel_Cs_Sort", "Excel_Cs_SortV1", "Excel_Cs_Filter", "Excel_Cs_ExcelFilter", "Excel_Cs_ClearExcelFilter", "Excel_Cs_EnableExcelFilter", "Excel_Cs_DeleteDuplicates", "Excel_CopyRange", "Excel_Cs_CopyRange", "Excel_Cs_CopyRangeV2", "Excel_Cs_CopyRangeV3", "Excel_PasteRange", "Excel_Cs_PastRange", "Excel_Cs_PastRangeV2", "Excel_CreateSheet", "Excel_DeleteSheet", "Excel_Cs_Delete", "Excel_ActiveSheet", "Excel_Cs_Active", "Excel_Cs_GetName", "Excel_SetCellFontColor", "Excel_Cs_SetCellFont", "Excel_SetRangeFontColor", "Excel_Cs_SetRangeFont", "Excel_Cs_SetRangeFontV2", "Excel_SetCellColor", "Excel_Cs_SetCellBackground", "Excel_SetRangeColor", "Excel_Cs_SetRangeBackground", "Excel_Cs_SetRangeBackgroundV2", "Excel_Cs_ReadCellBackground", "Excel_Cs_AutoFill", "Excel_Cs_AutoFillV2", "Excel_Cs_ColumnSplit", "Excel_Cs_ColumnSplitV2", "Excel_Close", "Excel_Cs_Close", "Excel_DeleteRange", "Excel_Cs_DeleteRange", "Excel_Cs_DeleteRangeV2", "Excel_Cs_DeleteRangeV3" ] as const; registerMany(["Excel_WriteRow", "Excel_WriteRowNew", "Excel_Cs_WriteRow", "Excel_Cs_WriteRowV2"], writeRow); registerMany(["Excel_WriteColumn", "Excel_WriteColumnNew", "Excel_Cs_WriteColumn"], writeColumn); register("Excel_Cs_WriteRangeV2", writeRange); registerMany(["Excel_GetRowData", "Excel_GetRowDataNew", "Excel_Cs_ReadRow"], readRow); register("Excel_GetColData", readColumn); register("Excel_Cs_ReadColumnCount", readColumnCount); register("Excel_Cs_ReadRange", readRange); registerMany(["Excel_Cs_Find", "Excel_Cs_FindV2", "Excel_Cs_Search"], findOrReplace); registerMany(["Excel_Cs_Sort", "Excel_Cs_SortV1"], sortRange); registerMany(["Excel_Cs_Filter", "Excel_Cs_ExcelFilter"], filterRows); registerMany(["Excel_Cs_ClearExcelFilter", "Excel_Cs_EnableExcelFilter"], clearFilter); register("Excel_Cs_DeleteDuplicates", deleteDuplicates); registerMany(["Excel_CopyRange", "Excel_Cs_CopyRange", "Excel_Cs_CopyRangeV2", "Excel_Cs_CopyRangeV3"], copyRange); registerMany(["Excel_PasteRange", "Excel_Cs_PastRange", "Excel_Cs_PastRangeV2"], pasteRange); register("Excel_CreateSheet", createSheet); registerMany(["Excel_DeleteSheet", "Excel_Cs_Delete"], deleteSheet); registerMany(["Excel_ActiveSheet", "Excel_Cs_Active"], activateSheet); register("Excel_Cs_GetName", getSheetNames); registerMany(["Excel_SetCellFontColor", "Excel_Cs_SetCellFont"], setColor("font", false)); registerMany(["Excel_SetRangeFontColor", "Excel_Cs_SetRangeFont", "Excel_Cs_SetRangeFontV2"], setColor("font", true)); registerMany(["Excel_SetCellColor", "Excel_Cs_SetCellBackground"], setColor("background", false)); registerMany(["Excel_SetRangeColor", "Excel_Cs_SetRangeBackground", "Excel_Cs_SetRangeBackgroundV2"], setColor("background", true)); register("Excel_Cs_ReadCellBackground", readBackground); registerMany(["Excel_Cs_AutoFill", "Excel_Cs_AutoFillV2"], autoFill); registerMany(["Excel_Cs_ColumnSplit", "Excel_Cs_ColumnSplitV2"], splitColumn); registerMany(["Excel_Close", "Excel_Cs_Close"], closeWorkbook); registerMany(["Excel_DeleteRange", "Excel_Cs_DeleteRange", "Excel_Cs_DeleteRangeV2", "Excel_Cs_DeleteRangeV3"], deleteRange);