@gmickel/gno 1.45.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/THIRD_PARTY_NOTICES.md +46 -0
- package/assets/skill/SKILL.md +7 -6
- package/assets/skill/cli-reference.md +14 -6
- package/assets/skill/mcp-reference.md +4 -1
- package/assets/spa-production.json.gz +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.0.0.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.0.0.zip.sha256 +1 -0
- package/browser-extension/dist/chunk-4tc9v0ja.js +74 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/browser-extension/dist/preview.html +1 -1
- package/browser-extension/dist/service-worker.js +32 -33
- package/bunfig.toml +2 -0
- package/package.json +40 -26
- package/spec/cli.md +30 -11
- package/spec/db/schema.sql +146 -1
- package/spec/mcp.md +26 -0
- package/src/app/context-runtime-types.ts +3 -0
- package/src/app/context-runtime.ts +2 -0
- package/src/cli/commands/ask.ts +6 -1
- package/src/cli/commands/daemon.ts +21 -8
- package/src/cli/commands/embed.ts +77 -41
- package/src/cli/commands/mcp/install.ts +20 -0
- package/src/cli/commands/mcp/paths.ts +25 -0
- package/src/cli/commands/mcp/status.ts +6 -0
- package/src/cli/detach.ts +3 -2
- package/src/cli/program.ts +6 -0
- package/src/config/types.ts +3 -3
- package/src/converters/adapters/markitdownTs/adapter.ts +1 -2
- package/src/converters/adapters/officeparser/adapter.ts +1 -2
- package/src/converters/versions.ts +6 -8
- package/src/core/context-evidence.ts +8 -4
- package/src/core/job-manager.ts +95 -13
- package/src/core/network-boundary-inventory.ts +10 -0
- package/src/core/shutdown-budget.ts +45 -0
- package/src/embed/backlog.ts +107 -4
- package/src/embed/batch.ts +42 -2
- package/src/embed/fingerprint.ts +16 -0
- package/src/embed/retry.ts +113 -5
- package/src/embed/variant-backlog.ts +105 -0
- package/src/embed/variant-plan.ts +62 -0
- package/src/embed/variant-retry.ts +113 -0
- package/src/ingestion/graph-reconciliation.ts +327 -0
- package/src/ingestion/sync.ts +9 -272
- package/src/llm/http-inference.ts +6 -0
- package/src/llm/httpEmbedding.ts +37 -6
- package/src/llm/httpGeneration.ts +18 -3
- package/src/llm/httpRerank.ts +23 -5
- package/src/llm/inference-cancellation.ts +168 -0
- package/src/llm/inference-scope.ts +202 -0
- package/src/llm/lazy-ports.ts +115 -0
- package/src/llm/native-worker/client.ts +541 -0
- package/src/llm/native-worker/dispatcher.ts +228 -0
- package/src/llm/native-worker/embedding-identity.ts +33 -0
- package/src/llm/native-worker/entry.ts +173 -0
- package/src/llm/native-worker/errors.ts +32 -0
- package/src/llm/native-worker/evaluation.ts +16 -0
- package/src/llm/native-worker/owned-exit.ts +108 -0
- package/src/llm/native-worker/owner.ts +141 -0
- package/src/llm/native-worker/ports.ts +317 -0
- package/src/llm/native-worker/protocol.ts +442 -0
- package/src/llm/native-worker/runtime-config.ts +92 -0
- package/src/llm/nodeLlamaCpp/adapter.ts +77 -20
- package/src/llm/nodeLlamaCpp/embedding.ts +130 -46
- package/src/llm/nodeLlamaCpp/generation.ts +34 -5
- package/src/llm/nodeLlamaCpp/lifecycle-options.ts +99 -0
- package/src/llm/nodeLlamaCpp/lifecycle.ts +209 -204
- package/src/llm/nodeLlamaCpp/rerank-capacity.ts +111 -0
- package/src/llm/nodeLlamaCpp/rerank.ts +118 -27
- package/src/llm/nodeLlamaCpp/simulator-handle.ts +73 -0
- package/src/llm/nodeLlamaCpp/simulator-install.ts +124 -0
- package/src/llm/nodeLlamaCpp/simulator-session.ts +240 -0
- package/src/llm/nodeLlamaCpp/simulator-types.ts +80 -0
- package/src/llm/types.ts +35 -5
- package/src/mcp/context.ts +27 -0
- package/src/mcp/http-transport.ts +12 -10
- package/src/mcp/server.ts +3 -0
- package/src/mcp/tool-profile.ts +30 -8
- package/src/mcp/tools/context.ts +8 -11
- package/src/mcp/tools/embed.ts +1 -1
- package/src/mcp/tools/index-cmd.ts +1 -1
- package/src/mcp/tools/index.ts +10 -8
- package/src/mcp/tools/query.ts +14 -30
- package/src/mcp/tools/vsearch.ts +1 -1
- package/src/pipeline/answer.ts +23 -3
- package/src/pipeline/claim-verifier.ts +6 -0
- package/src/pipeline/expansion.ts +43 -40
- package/src/pipeline/explain.ts +6 -2
- package/src/pipeline/filters.ts +63 -0
- package/src/pipeline/fusion.ts +29 -9
- package/src/pipeline/graph-retrieval.ts +29 -9
- package/src/pipeline/hybrid.ts +198 -55
- package/src/pipeline/hydration.ts +161 -0
- package/src/pipeline/owner-fusion.ts +87 -0
- package/src/pipeline/rerank.ts +35 -11
- package/src/pipeline/search.ts +13 -2
- package/src/pipeline/types.ts +5 -3
- package/src/pipeline/vsearch.ts +87 -7
- package/src/sdk/client.ts +47 -3
- package/src/sdk/embed.ts +63 -39
- package/src/serve/background-runtime.ts +1 -1
- package/src/serve/context.ts +41 -56
- package/src/serve/embed-scheduler.ts +58 -35
- package/src/serve/public/components/IndexingProgress.tsx +46 -60
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/lib/shiki-language-ids.ts +14 -0
- package/src/serve/resident-admission.ts +36 -36
- package/src/serve/resident-background-work.ts +20 -2
- package/src/serve/resident-request.ts +11 -5
- package/src/serve/resident-runtime.ts +97 -61
- package/src/serve/resident-shutdown.ts +153 -0
- package/src/serve/routes/api.ts +3 -1
- package/src/serve/server.ts +47 -26
- package/src/store/migrations/028-vector-variants.ts +54 -0
- package/src/store/migrations/029-graph-reference-state.ts +77 -0
- package/src/store/migrations/index.ts +4 -0
- package/src/store/sqlite/adapter.ts +251 -183
- package/src/store/sqlite/eligibility.ts +174 -0
- package/src/store/sqlite/graph-edge-application.ts +66 -0
- package/src/store/sqlite/graph-reference-state.ts +194 -0
- package/src/store/sqlite/legacy-vector-ownership.ts +79 -0
- package/src/store/types.ts +80 -12
- package/src/store/vector/eligibility.ts +36 -0
- package/src/store/vector/freshness.ts +33 -6
- package/src/store/vector/lazy.ts +81 -0
- package/src/store/vector/sqlite-vec.ts +106 -54
- package/src/store/vector/stats.ts +14 -3
- package/src/store/vector/types.ts +35 -2
- package/src/store/vector/variant-search.ts +192 -0
- package/src/store/vector/variants.ts +451 -0
- package/vendor/converters/markitdown-ts/LICENSE +21 -0
- package/vendor/converters/markitdown-ts/dist/index.cjs +1180 -0
- package/vendor/converters/markitdown-ts/dist/index.d.cts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.d.mts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.d.ts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.mjs +1152 -0
- package/vendor/converters/markitdown-ts/package.json +77 -0
- package/vendor/converters/officeparser/LICENSE +21 -0
- package/vendor/converters/officeparser/dist/OfficeConverter.d.ts +47 -0
- package/vendor/converters/officeparser/dist/OfficeConverter.js +76 -0
- package/vendor/converters/officeparser/dist/OfficeGenerator.d.ts +23 -0
- package/vendor/converters/officeparser/dist/OfficeGenerator.js +73 -0
- package/vendor/converters/officeparser/dist/OfficeParser.d.ts +106 -0
- package/vendor/converters/officeparser/dist/OfficeParser.js +332 -0
- package/vendor/converters/officeparser/dist/cli.d.ts +28 -0
- package/vendor/converters/officeparser/dist/cli.js +381 -0
- package/vendor/converters/officeparser/dist/defaults.d.ts +41 -0
- package/vendor/converters/officeparser/dist/defaults.js +218 -0
- package/vendor/converters/officeparser/dist/generators/BaseGenerator.d.ts +107 -0
- package/vendor/converters/officeparser/dist/generators/BaseGenerator.js +248 -0
- package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.d.ts +82 -0
- package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.js +797 -0
- package/vendor/converters/officeparser/dist/generators/CsvGenerator.d.ts +38 -0
- package/vendor/converters/officeparser/dist/generators/CsvGenerator.js +245 -0
- package/vendor/converters/officeparser/dist/generators/EpubGenerator.d.ts +43 -0
- package/vendor/converters/officeparser/dist/generators/EpubGenerator.js +315 -0
- package/vendor/converters/officeparser/dist/generators/HtmlGenerator.d.ts +59 -0
- package/vendor/converters/officeparser/dist/generators/HtmlGenerator.js +1942 -0
- package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.d.ts +96 -0
- package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.js +1175 -0
- package/vendor/converters/officeparser/dist/generators/PdfGenerator.d.ts +22 -0
- package/vendor/converters/officeparser/dist/generators/PdfGenerator.js +194 -0
- package/vendor/converters/officeparser/dist/generators/RtfGenerator.d.ts +29 -0
- package/vendor/converters/officeparser/dist/generators/RtfGenerator.js +316 -0
- package/vendor/converters/officeparser/dist/generators/TextGenerator.d.ts +13 -0
- package/vendor/converters/officeparser/dist/generators/TextGenerator.js +201 -0
- package/vendor/converters/officeparser/dist/index.d.ts +60 -0
- package/vendor/converters/officeparser/dist/index.js +72 -0
- package/vendor/converters/officeparser/dist/index.mjs +18 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.d.ts +2621 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.iife.js +1336 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.mjs +1335 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.d.ts +2621 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.iife.js +1336 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.mjs +1335 -0
- package/vendor/converters/officeparser/dist/parsers/CsvParser.d.ts +9 -0
- package/vendor/converters/officeparser/dist/parsers/CsvParser.js +115 -0
- package/vendor/converters/officeparser/dist/parsers/EpubParser.d.ts +8 -0
- package/vendor/converters/officeparser/dist/parsers/EpubParser.js +217 -0
- package/vendor/converters/officeparser/dist/parsers/ExcelParser.d.ts +32 -0
- package/vendor/converters/officeparser/dist/parsers/ExcelParser.js +736 -0
- package/vendor/converters/officeparser/dist/parsers/HtmlParser.d.ts +2 -0
- package/vendor/converters/officeparser/dist/parsers/HtmlParser.js +1287 -0
- package/vendor/converters/officeparser/dist/parsers/MarkdownParser.d.ts +2 -0
- package/vendor/converters/officeparser/dist/parsers/MarkdownParser.js +1272 -0
- package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.d.ts +31 -0
- package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.js +1819 -0
- package/vendor/converters/officeparser/dist/parsers/PdfParser.d.ts +67 -0
- package/vendor/converters/officeparser/dist/parsers/PdfParser.js +848 -0
- package/vendor/converters/officeparser/dist/parsers/PowerPointParser.d.ts +32 -0
- package/vendor/converters/officeparser/dist/parsers/PowerPointParser.js +950 -0
- package/vendor/converters/officeparser/dist/parsers/RtfParser.d.ts +187 -0
- package/vendor/converters/officeparser/dist/parsers/RtfParser.js +1801 -0
- package/vendor/converters/officeparser/dist/parsers/WordParser.d.ts +79 -0
- package/vendor/converters/officeparser/dist/parsers/WordParser.js +1177 -0
- package/vendor/converters/officeparser/dist/sbom.cdx.json +1763 -0
- package/vendor/converters/officeparser/dist/types.d.ts +2507 -0
- package/vendor/converters/officeparser/dist/types.js +107 -0
- package/vendor/converters/officeparser/dist/utils/astUtils.d.ts +16 -0
- package/vendor/converters/officeparser/dist/utils/astUtils.js +33 -0
- package/vendor/converters/officeparser/dist/utils/chartUtils.d.ts +6 -0
- package/vendor/converters/officeparser/dist/utils/chartUtils.js +257 -0
- package/vendor/converters/officeparser/dist/utils/configUtils.d.ts +44 -0
- package/vendor/converters/officeparser/dist/utils/configUtils.js +315 -0
- package/vendor/converters/officeparser/dist/utils/dateUtils.d.ts +17 -0
- package/vendor/converters/officeparser/dist/utils/dateUtils.js +69 -0
- package/vendor/converters/officeparser/dist/utils/envUtils.d.ts +29 -0
- package/vendor/converters/officeparser/dist/utils/envUtils.js +152 -0
- package/vendor/converters/officeparser/dist/utils/errorUtils.d.ts +72 -0
- package/vendor/converters/officeparser/dist/utils/errorUtils.js +245 -0
- package/vendor/converters/officeparser/dist/utils/imageUtils.d.ts +66 -0
- package/vendor/converters/officeparser/dist/utils/imageUtils.js +133 -0
- package/vendor/converters/officeparser/dist/utils/mathUtils.d.ts +42 -0
- package/vendor/converters/officeparser/dist/utils/mathUtils.js +385 -0
- package/vendor/converters/officeparser/dist/utils/moduleLoader.d.ts +18 -0
- package/vendor/converters/officeparser/dist/utils/moduleLoader.js +106 -0
- package/vendor/converters/officeparser/dist/utils/ocrUtils.d.ts +42 -0
- package/vendor/converters/officeparser/dist/utils/ocrUtils.js +428 -0
- package/vendor/converters/officeparser/dist/utils/sanitize.d.ts +148 -0
- package/vendor/converters/officeparser/dist/utils/sanitize.js +344 -0
- package/vendor/converters/officeparser/dist/utils/sheetUtils.d.ts +7 -0
- package/vendor/converters/officeparser/dist/utils/sheetUtils.js +35 -0
- package/vendor/converters/officeparser/dist/utils/styleMapper.d.ts +36 -0
- package/vendor/converters/officeparser/dist/utils/styleMapper.js +224 -0
- package/vendor/converters/officeparser/dist/utils/xmlUtils.d.ts +163 -0
- package/vendor/converters/officeparser/dist/utils/xmlUtils.js +461 -0
- package/vendor/converters/officeparser/dist/utils/zipUtils.d.ts +134 -0
- package/vendor/converters/officeparser/dist/utils/zipUtils.js +337 -0
- package/vendor/converters/officeparser/package.json +147 -0
- package/vendor/converters/upstream-manifest.json +124 -0
- package/vendor/dependency-fixes/README.md +77 -0
- package/vendor/dependency-fixes/vendor-converters.py +83 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.45.1.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.45.1.zip.sha256 +0 -1
- package/browser-extension/dist/chunk-627emwpj.js +0 -75
- /package/browser-extension/dist/{chunk-ydfx5d7p.css → chunk-z74y8n8c.css} +0 -0
|
@@ -0,0 +1,736 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Excel Spreadsheet (XLSX) Parser
|
|
4
|
+
*
|
|
5
|
+
* **XLSX Format Overview:**
|
|
6
|
+
* XLSX is the default format for Microsoft Excel since Office 2007, based on OOXML.
|
|
7
|
+
*
|
|
8
|
+
* **File Structure:**
|
|
9
|
+
* - `xl/workbook.xml` - Workbook structure and sheet list
|
|
10
|
+
* - `xl/worksheets/sheet1.xml` - Individual sheet data
|
|
11
|
+
* - `xl/sharedStrings.xml` - Shared string table (cell text)
|
|
12
|
+
* - `xl/styles.xml` - Cell styling information
|
|
13
|
+
* - `xl/drawings/*` - Charts and drawings
|
|
14
|
+
* - `xl/media/*` - Embedded images
|
|
15
|
+
*
|
|
16
|
+
* **Key Elements:**
|
|
17
|
+
* - `<row>` - Table row with row index
|
|
18
|
+
* - `<c r="A1">` - Cell with reference (A1, B2, etc.)
|
|
19
|
+
* - `<v>` - Cell value (number or shared string index)
|
|
20
|
+
* - `<t="s">` - Cell type (s=string, n=number, b=boolean)
|
|
21
|
+
*
|
|
22
|
+
* @module ExcelParser
|
|
23
|
+
* @see https://www.ecma-international.org/publications-and-standards/standards/ecma-376/
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.parseExcel = void 0;
|
|
27
|
+
const types_js_1 = require("../types.js");
|
|
28
|
+
const astUtils_js_1 = require("../utils/astUtils.js");
|
|
29
|
+
const chartUtils_js_1 = require("../utils/chartUtils.js");
|
|
30
|
+
const errorUtils_js_1 = require("../utils/errorUtils.js");
|
|
31
|
+
const imageUtils_js_1 = require("../utils/imageUtils.js");
|
|
32
|
+
const ocrUtils_js_1 = require("../utils/ocrUtils.js");
|
|
33
|
+
const xmlUtils_js_1 = require("../utils/xmlUtils.js");
|
|
34
|
+
const zipUtils_js_1 = require("../utils/zipUtils.js");
|
|
35
|
+
/**
|
|
36
|
+
* Parses an Excel spreadsheet (.xlsx) and extracts sheets, rows, and cells.
|
|
37
|
+
*
|
|
38
|
+
* @param buffer - The XLSX file as a Buffer
|
|
39
|
+
* @param config - Parser configuration
|
|
40
|
+
* @returns A promise resolving to the parsed AST
|
|
41
|
+
*/
|
|
42
|
+
const parseExcel = async (buffer, config) => {
|
|
43
|
+
// Honour cancellation requests immediately — before extracting the ZIP archive.
|
|
44
|
+
// XLSX parsing involves decompressing multiple XML sheets and potentially running OCR
|
|
45
|
+
// on embedded chart images, so short-circuiting here saves significant work.
|
|
46
|
+
(0, errorUtils_js_1.checkAbortSignal)(config.abortSignal);
|
|
47
|
+
const sheetsRegex = /xl\/worksheets\/sheet\d+.xml/g;
|
|
48
|
+
const drawingsRegex = /xl\/drawings\/drawing\d+.xml/g;
|
|
49
|
+
const chartsRegex = /xl\/charts\/chart\d+.xml/g;
|
|
50
|
+
const stringsFilePath = 'xl/sharedStrings.xml';
|
|
51
|
+
const mediaFileRegex = /xl\/media\/.*/;
|
|
52
|
+
const corePropsFileRegex = /docProps\/core\.xml/;
|
|
53
|
+
const customPropsFileRegex = /docProps\/custom\.xml/;
|
|
54
|
+
const appPropsFileRegex = /docProps\/app\.xml/;
|
|
55
|
+
const relsRegex = /xl\/worksheets\/_rels\/sheet\d+\.xml\.rels/g;
|
|
56
|
+
const drawingRelsRegex = /xl\/drawings\/_rels\/drawing\d+\.xml\.rels/g;
|
|
57
|
+
const commentsRegex = /xl\/comments\d+\.xml/g;
|
|
58
|
+
const files = await (0, zipUtils_js_1.extractFiles)(buffer, (x) => !!x.match(sheetsRegex) ||
|
|
59
|
+
!!x.match(drawingsRegex) ||
|
|
60
|
+
!!x.match(chartsRegex) ||
|
|
61
|
+
(!config.ignoreComments && !!x.match(commentsRegex)) ||
|
|
62
|
+
x === stringsFilePath ||
|
|
63
|
+
x === 'xl/styles.xml' ||
|
|
64
|
+
x === 'xl/workbook.xml' ||
|
|
65
|
+
x === 'xl/_rels/workbook.xml.rels' ||
|
|
66
|
+
!!x.match(corePropsFileRegex) ||
|
|
67
|
+
!!x.match(customPropsFileRegex) ||
|
|
68
|
+
!!x.match(appPropsFileRegex) ||
|
|
69
|
+
(!!config.extractAttachments && (!!x.match(mediaFileRegex) || !!x.match(drawingRelsRegex))) ||
|
|
70
|
+
((!!config.extractAttachments || !config.ignoreComments) && !!x.match(relsRegex)), config.decompressionLimits, config);
|
|
71
|
+
// Every workbook has xl/workbook.xml; without it the archive is not a spreadsheet.
|
|
72
|
+
// Resolved up front so a file that cannot be a workbook fails before any of the parsing
|
|
73
|
+
// work below, and read again further down for the sheet-name map.
|
|
74
|
+
const workbookFile = (0, zipUtils_js_1.findRequiredPart)(files, path => path === 'xl/workbook.xml', config, { fileType: 'xlsx', part: 'xl/workbook.xml' });
|
|
75
|
+
// Worksheets, by contrast, are not guaranteed: a workbook holding only chartsheets is
|
|
76
|
+
// valid and simply has no cell text to extract. Warn rather than fail, so the caller can
|
|
77
|
+
// tell "nothing to read here" from "we read nothing".
|
|
78
|
+
if (!files.some(file => !!file.path.match(sheetsRegex)))
|
|
79
|
+
(0, errorUtils_js_1.logWarning)(types_js_1.OfficeWarningType.NO_WORKSHEETS_FOUND, config);
|
|
80
|
+
const sharedStringsFile = files.find(f => f.path === stringsFilePath);
|
|
81
|
+
// Updated to store structured content (rich text runs) or simple string
|
|
82
|
+
const sharedStrings = [];
|
|
83
|
+
if (sharedStringsFile) {
|
|
84
|
+
const xml = (0, xmlUtils_js_1.parseXmlString)(sharedStringsFile.content.toString());
|
|
85
|
+
const siNodes = (0, xmlUtils_js_1.getElementsByTagName)(xml, "si");
|
|
86
|
+
for (const si of siNodes) {
|
|
87
|
+
const runNodes = (0, xmlUtils_js_1.getElementsByTagName)(si, "r");
|
|
88
|
+
if (runNodes.length > 0) {
|
|
89
|
+
// Rich text with runs
|
|
90
|
+
const runs = [];
|
|
91
|
+
for (const run of runNodes) {
|
|
92
|
+
const tNode = (0, xmlUtils_js_1.getElementsByTagName)(run, "t")[0];
|
|
93
|
+
if (tNode) {
|
|
94
|
+
const text = tNode.textContent || '';
|
|
95
|
+
// Extract run formatting
|
|
96
|
+
const rPr = (0, xmlUtils_js_1.getElementsByTagName)(run, "rPr")[0];
|
|
97
|
+
const formatting = {};
|
|
98
|
+
if (rPr) {
|
|
99
|
+
if ((0, xmlUtils_js_1.getElementsByTagName)(rPr, "b").length > 0)
|
|
100
|
+
formatting.bold = true;
|
|
101
|
+
if ((0, xmlUtils_js_1.getElementsByTagName)(rPr, "i").length > 0)
|
|
102
|
+
formatting.italic = true;
|
|
103
|
+
if ((0, xmlUtils_js_1.getElementsByTagName)(rPr, "u").length > 0)
|
|
104
|
+
formatting.underline = true;
|
|
105
|
+
if ((0, xmlUtils_js_1.getElementsByTagName)(rPr, "strike").length > 0)
|
|
106
|
+
formatting.strikethrough = true;
|
|
107
|
+
const sz = (0, xmlUtils_js_1.getElementsByTagName)(rPr, "sz")[0];
|
|
108
|
+
if (sz)
|
|
109
|
+
formatting.size = sz.getAttribute("val") + 'pt';
|
|
110
|
+
const color = (0, xmlUtils_js_1.getElementsByTagName)(rPr, "color")[0];
|
|
111
|
+
if (color) {
|
|
112
|
+
const rgb = color.getAttribute("rgb");
|
|
113
|
+
if (rgb)
|
|
114
|
+
formatting.color = '#' + rgb.substring(2);
|
|
115
|
+
}
|
|
116
|
+
const rFont = (0, xmlUtils_js_1.getElementsByTagName)(rPr, "rFont")[0];
|
|
117
|
+
if (rFont)
|
|
118
|
+
formatting.font = rFont.getAttribute("val") || undefined;
|
|
119
|
+
const vertAlign = (0, xmlUtils_js_1.getElementsByTagName)(rPr, "vertAlign")[0];
|
|
120
|
+
if (vertAlign) {
|
|
121
|
+
const val = vertAlign.getAttribute("val");
|
|
122
|
+
if (val === "subscript")
|
|
123
|
+
formatting.subscript = true;
|
|
124
|
+
if (val === "superscript")
|
|
125
|
+
formatting.superscript = true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
runs.push({
|
|
129
|
+
type: 'text',
|
|
130
|
+
text: text,
|
|
131
|
+
formatting: Object.keys(formatting).length > 0 ? formatting : undefined
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
sharedStrings.push(runs);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
// Simple text case
|
|
139
|
+
const tNodes = (0, xmlUtils_js_1.getElementsByTagName)(si, "t");
|
|
140
|
+
let text = '';
|
|
141
|
+
for (const t of tNodes) {
|
|
142
|
+
text += t.textContent || '';
|
|
143
|
+
}
|
|
144
|
+
sharedStrings.push(text);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Parse styles to build formatting map
|
|
149
|
+
const stylesFile = files.find(f => f.path === 'xl/styles.xml');
|
|
150
|
+
const cellFormatMap = {};
|
|
151
|
+
if (stylesFile) {
|
|
152
|
+
const xml = (0, xmlUtils_js_1.parseXmlString)(stylesFile.content.toString());
|
|
153
|
+
// Parse fonts
|
|
154
|
+
const fontsNode = (0, xmlUtils_js_1.getElementsByTagName)(xml, "fonts")[0];
|
|
155
|
+
const fonts = [];
|
|
156
|
+
if (fontsNode) {
|
|
157
|
+
const fontNodes = (0, xmlUtils_js_1.getElementsByTagName)(fontsNode, "font");
|
|
158
|
+
for (const font of fontNodes) {
|
|
159
|
+
const formatting = {};
|
|
160
|
+
if ((0, xmlUtils_js_1.getElementsByTagName)(font, "b").length > 0)
|
|
161
|
+
formatting.bold = true;
|
|
162
|
+
if ((0, xmlUtils_js_1.getElementsByTagName)(font, "i").length > 0)
|
|
163
|
+
formatting.italic = true;
|
|
164
|
+
if ((0, xmlUtils_js_1.getElementsByTagName)(font, "u").length > 0)
|
|
165
|
+
formatting.underline = true;
|
|
166
|
+
if ((0, xmlUtils_js_1.getElementsByTagName)(font, "strike").length > 0)
|
|
167
|
+
formatting.strikethrough = true;
|
|
168
|
+
const szNode = (0, xmlUtils_js_1.getElementsByTagName)(font, "sz")[0];
|
|
169
|
+
if (szNode) {
|
|
170
|
+
const val = szNode.getAttribute("val");
|
|
171
|
+
if (val)
|
|
172
|
+
formatting.size = val + 'pt';
|
|
173
|
+
}
|
|
174
|
+
const colorNode = (0, xmlUtils_js_1.getElementsByTagName)(font, "color")[0];
|
|
175
|
+
if (colorNode) {
|
|
176
|
+
const rgb = colorNode.getAttribute("rgb");
|
|
177
|
+
if (rgb)
|
|
178
|
+
formatting.color = '#' + rgb.substring(2); // Remove alpha channel
|
|
179
|
+
}
|
|
180
|
+
const nameNode = (0, xmlUtils_js_1.getElementsByTagName)(font, "name")[0];
|
|
181
|
+
if (nameNode) {
|
|
182
|
+
const val = nameNode.getAttribute("val");
|
|
183
|
+
if (val)
|
|
184
|
+
formatting.font = val;
|
|
185
|
+
}
|
|
186
|
+
const vertAlignNode = (0, xmlUtils_js_1.getElementsByTagName)(font, "vertAlign")[0];
|
|
187
|
+
if (vertAlignNode) {
|
|
188
|
+
const val = vertAlignNode.getAttribute("val");
|
|
189
|
+
if (val === "subscript")
|
|
190
|
+
formatting.subscript = true;
|
|
191
|
+
if (val === "superscript")
|
|
192
|
+
formatting.superscript = true;
|
|
193
|
+
}
|
|
194
|
+
fonts.push(formatting);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Parse fills (for background color)
|
|
198
|
+
const fillsNode = (0, xmlUtils_js_1.getElementsByTagName)(xml, "fills")[0];
|
|
199
|
+
const fills = [];
|
|
200
|
+
if (fillsNode) {
|
|
201
|
+
const fillNodes = (0, xmlUtils_js_1.getElementsByTagName)(fillsNode, "fill");
|
|
202
|
+
for (const fill of fillNodes) {
|
|
203
|
+
const formatting = {};
|
|
204
|
+
const patternFill = (0, xmlUtils_js_1.getElementsByTagName)(fill, "patternFill")[0];
|
|
205
|
+
if (patternFill) {
|
|
206
|
+
const fgColor = (0, xmlUtils_js_1.getElementsByTagName)(patternFill, "fgColor")[0];
|
|
207
|
+
if (fgColor) {
|
|
208
|
+
const rgb = fgColor.getAttribute("rgb");
|
|
209
|
+
const theme = fgColor.getAttribute("theme");
|
|
210
|
+
if (rgb && rgb !== "00000000") { // Not default/auto
|
|
211
|
+
formatting.backgroundColor = '#' + rgb.substring(2);
|
|
212
|
+
}
|
|
213
|
+
else if (theme) {
|
|
214
|
+
// Basic mapping for standard Office themes (Dark 1, Light 1, Dark 2, Light 2)
|
|
215
|
+
// 0: Light 1 (White), 1: Dark 1 (Black), 2: Light 2 (Tan/Gray), 3: Dark 2 (Blue/Grey)
|
|
216
|
+
const themeIdx = parseInt(theme);
|
|
217
|
+
if (themeIdx === 0)
|
|
218
|
+
formatting.backgroundColor = '#FFFFFF';
|
|
219
|
+
else if (themeIdx === 1)
|
|
220
|
+
formatting.backgroundColor = '#000000';
|
|
221
|
+
else if (themeIdx === 2)
|
|
222
|
+
formatting.backgroundColor = '#EEECE1'; // Standard Light 2
|
|
223
|
+
else if (themeIdx === 3)
|
|
224
|
+
formatting.backgroundColor = '#1F497D'; // Standard Dark 2
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
fills.push(formatting);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Parse cellXfs (cell format definitions)
|
|
232
|
+
const cellXfsNode = (0, xmlUtils_js_1.getElementsByTagName)(xml, "cellXfs")[0];
|
|
233
|
+
if (cellXfsNode) {
|
|
234
|
+
const xfNodes = (0, xmlUtils_js_1.getElementsByTagName)(cellXfsNode, "xf");
|
|
235
|
+
for (let i = 0; i < xfNodes.length; i++) {
|
|
236
|
+
const xf = xfNodes[i];
|
|
237
|
+
const formatting = {};
|
|
238
|
+
const fontId = xf.getAttribute("fontId");
|
|
239
|
+
if (fontId) {
|
|
240
|
+
const fontIdx = parseInt(fontId);
|
|
241
|
+
if (fonts[fontIdx]) {
|
|
242
|
+
Object.assign(formatting, fonts[fontIdx]);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const fillId = xf.getAttribute("fillId");
|
|
246
|
+
if (fillId) {
|
|
247
|
+
const fillIdx = parseInt(fillId);
|
|
248
|
+
if (fills[fillIdx] && fills[fillIdx].backgroundColor) {
|
|
249
|
+
formatting.backgroundColor = fills[fillIdx].backgroundColor;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const alignmentNode = (0, xmlUtils_js_1.getElementsByTagName)(xf, "alignment")[0];
|
|
253
|
+
if (alignmentNode) {
|
|
254
|
+
const horizontal = alignmentNode.getAttribute("horizontal");
|
|
255
|
+
if (horizontal === 'center' || horizontal === 'right' || horizontal === 'justify' || horizontal === 'left') {
|
|
256
|
+
formatting.alignment = horizontal;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
cellFormatMap[i] = formatting;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const attachments = [];
|
|
264
|
+
const mediaFiles = files.filter(f => f.path.match(/xl\/media\/.*/));
|
|
265
|
+
const chartFiles = files.filter(f => f.path.match(chartsRegex));
|
|
266
|
+
// Map to store image details by drawing file path and relationship ID
|
|
267
|
+
const drawingImageMap = {};
|
|
268
|
+
if (config.extractAttachments) {
|
|
269
|
+
// 1. Parse Drawing Rels to map rIds to media paths
|
|
270
|
+
const drawingRelsFiles = files.filter(f => f.path.match(drawingRelsRegex));
|
|
271
|
+
for (const relFile of drawingRelsFiles) {
|
|
272
|
+
const drawingFilename = relFile.path.split('/').pop()?.replace('.rels', '') || '';
|
|
273
|
+
const drawingPath = `xl/drawings/${drawingFilename}`;
|
|
274
|
+
const relsXml = (0, xmlUtils_js_1.parseXmlString)(relFile.content.toString());
|
|
275
|
+
const relationships = (0, xmlUtils_js_1.getElementsByTagName)(relsXml, "Relationship");
|
|
276
|
+
if (!drawingImageMap[drawingPath]) {
|
|
277
|
+
drawingImageMap[drawingPath] = {};
|
|
278
|
+
}
|
|
279
|
+
for (const rel of relationships) {
|
|
280
|
+
const id = rel.getAttribute("Id");
|
|
281
|
+
const target = rel.getAttribute("Target");
|
|
282
|
+
if (id && target && target.includes('media/')) {
|
|
283
|
+
// Target is usually like "../media/image1.png"
|
|
284
|
+
const mediaPath = 'xl/' + target.replace('../', '');
|
|
285
|
+
drawingImageMap[drawingPath][id] = { path: mediaPath };
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// 2. Parse Drawings to get Alt Text and link to Rels
|
|
290
|
+
const drawingFiles = files.filter(f => f.path.match(drawingsRegex));
|
|
291
|
+
for (const drawingFile of drawingFiles) {
|
|
292
|
+
const xml = (0, xmlUtils_js_1.parseXmlString)(drawingFile.content.toString());
|
|
293
|
+
const pics = (0, xmlUtils_js_1.getElementsByTagName)(xml, "xdr:pic"); // SpreadsheetML drawing
|
|
294
|
+
const rels = drawingImageMap[drawingFile.path] || {};
|
|
295
|
+
for (const pic of pics) {
|
|
296
|
+
const blipFill = (0, xmlUtils_js_1.getElementsByTagName)(pic, "xdr:blipFill")[0];
|
|
297
|
+
const blip = blipFill ? (0, xmlUtils_js_1.getElementsByTagName)(blipFill, "a:blip")[0] : null;
|
|
298
|
+
const embedId = blip ? blip.getAttribute("r:embed") : null;
|
|
299
|
+
const nvPicPr = (0, xmlUtils_js_1.getElementsByTagName)(pic, "xdr:nvPicPr")[0];
|
|
300
|
+
const cNvPr = nvPicPr ? (0, xmlUtils_js_1.getElementsByTagName)(nvPicPr, "xdr:cNvPr")[0] : null;
|
|
301
|
+
const altText = cNvPr ? (cNvPr.getAttribute("descr") || cNvPr.getAttribute("name")) : undefined;
|
|
302
|
+
if (embedId && rels[embedId]) {
|
|
303
|
+
rels[embedId].altText = altText || '';
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// 3. Process Media Files
|
|
308
|
+
for (const media of mediaFiles) {
|
|
309
|
+
const attachment = (0, imageUtils_js_1.createAttachment)(media.path.split('/').pop() || 'image', media.content);
|
|
310
|
+
// Try to find alt text for this media
|
|
311
|
+
let altText = '';
|
|
312
|
+
for (const drawingPath in drawingImageMap) {
|
|
313
|
+
for (const rId in drawingImageMap[drawingPath]) {
|
|
314
|
+
if (drawingImageMap[drawingPath][rId].path === media.path) {
|
|
315
|
+
altText = drawingImageMap[drawingPath][rId].altText || '';
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (altText)
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
if (altText)
|
|
323
|
+
attachment.altText = altText;
|
|
324
|
+
attachments.push(attachment);
|
|
325
|
+
if (config.ocr) {
|
|
326
|
+
if (attachment.mimeType.startsWith('image/')) {
|
|
327
|
+
try {
|
|
328
|
+
const ocrText = (await (0, ocrUtils_js_1.performOcr)(media.content, { ...config.ocrConfig })).trim();
|
|
329
|
+
if (ocrText) {
|
|
330
|
+
attachment.ocrText = ocrText;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
catch (e) {
|
|
334
|
+
(0, errorUtils_js_1.logWarning)(types_js_1.OfficeWarningType.OCR_FAILED, config, attachment.name, e);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
for (const chart of chartFiles) {
|
|
340
|
+
const attachment = {
|
|
341
|
+
type: 'chart',
|
|
342
|
+
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
343
|
+
data: chart.content.toString('base64'),
|
|
344
|
+
name: chart.path.split('/').pop() || '',
|
|
345
|
+
extension: 'xml'
|
|
346
|
+
};
|
|
347
|
+
// Extract structured chart data
|
|
348
|
+
try {
|
|
349
|
+
const chartData = (0, chartUtils_js_1.extractChartData)(chart.content);
|
|
350
|
+
attachment.chartData = chartData;
|
|
351
|
+
}
|
|
352
|
+
catch (e) {
|
|
353
|
+
(0, errorUtils_js_1.logWarning)(types_js_1.OfficeWarningType.CHART_DATA_EXTRACTION_FAILED, config, chart.path, e);
|
|
354
|
+
}
|
|
355
|
+
attachments.push(attachment);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// Build map of drawing rId -> chart attachment name for linking
|
|
359
|
+
const drawingChartMap = {};
|
|
360
|
+
if (config.extractAttachments) {
|
|
361
|
+
const drawingRelsFiles = files.filter(f => f.path.match(drawingRelsRegex));
|
|
362
|
+
for (const relFile of drawingRelsFiles) {
|
|
363
|
+
const drawingFilename = relFile.path.split('/').pop()?.replace('.rels', '') || '';
|
|
364
|
+
const drawingPath = `xl/drawings/${drawingFilename}`;
|
|
365
|
+
const relsXml = (0, xmlUtils_js_1.parseXmlString)(relFile.content.toString());
|
|
366
|
+
const relationships = (0, xmlUtils_js_1.getElementsByTagName)(relsXml, "Relationship");
|
|
367
|
+
if (!drawingChartMap[drawingPath]) {
|
|
368
|
+
drawingChartMap[drawingPath] = {};
|
|
369
|
+
}
|
|
370
|
+
for (const rel of relationships) {
|
|
371
|
+
const id = rel.getAttribute("Id");
|
|
372
|
+
const target = rel.getAttribute("Target");
|
|
373
|
+
const type = rel.getAttribute("Type");
|
|
374
|
+
if (id && target && type && type.includes('chart')) {
|
|
375
|
+
// Target is like "../charts/chart1.xml"
|
|
376
|
+
const chartName = target.split('/').pop() || '';
|
|
377
|
+
drawingChartMap[drawingPath][id] = chartName;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// Parse workbook.xml to get sheet names and map them to sheet files
|
|
383
|
+
const sheetNameMap = {};
|
|
384
|
+
const workbookRelsFile = files.find(f => f.path === 'xl/_rels/workbook.xml.rels');
|
|
385
|
+
if (workbookRelsFile) {
|
|
386
|
+
// Parse rels to get rId -> file mapping
|
|
387
|
+
const relsXml = (0, xmlUtils_js_1.parseXmlString)(workbookRelsFile.content.toString());
|
|
388
|
+
const relationships = (0, xmlUtils_js_1.getElementsByTagName)(relsXml, "Relationship");
|
|
389
|
+
const rIdToFile = {};
|
|
390
|
+
for (const rel of relationships) {
|
|
391
|
+
const rId = rel.getAttribute("Id");
|
|
392
|
+
const target = rel.getAttribute("Target");
|
|
393
|
+
if (rId && target) {
|
|
394
|
+
// Target is like "worksheets/sheet1.xml"
|
|
395
|
+
const filename = target.split('/').pop() || '';
|
|
396
|
+
rIdToFile[rId] = filename;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
// Parse workbook.xml to get sheet name -> rId mapping
|
|
400
|
+
const workbookXml = (0, xmlUtils_js_1.parseXmlString)(workbookFile.content.toString());
|
|
401
|
+
const sheets = (0, xmlUtils_js_1.getElementsByTagName)(workbookXml, "sheet");
|
|
402
|
+
for (const sheet of sheets) {
|
|
403
|
+
(0, errorUtils_js_1.checkAbortSignal)(config.abortSignal);
|
|
404
|
+
const name = sheet.getAttribute("name");
|
|
405
|
+
const rId = sheet.getAttribute("r:id");
|
|
406
|
+
if (name && rId && rIdToFile[rId]) {
|
|
407
|
+
sheetNameMap[rIdToFile[rId]] = name;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
const content = [];
|
|
412
|
+
for (const file of files) {
|
|
413
|
+
if (file.path.match(mediaFileRegex))
|
|
414
|
+
continue;
|
|
415
|
+
if (file.path === stringsFilePath)
|
|
416
|
+
continue;
|
|
417
|
+
if (file.path === 'xl/styles.xml')
|
|
418
|
+
continue;
|
|
419
|
+
if (file.path.match(drawingsRegex))
|
|
420
|
+
continue;
|
|
421
|
+
if (file.path.match(chartsRegex))
|
|
422
|
+
continue;
|
|
423
|
+
if (file.path.match(relsRegex))
|
|
424
|
+
continue;
|
|
425
|
+
if (file.path.match(drawingRelsRegex))
|
|
426
|
+
continue;
|
|
427
|
+
if (file.path.match(sheetsRegex)) {
|
|
428
|
+
const sheetFilename = file.path.split('/').pop() || '';
|
|
429
|
+
const relsFilename = `xl/worksheets/_rels/${sheetFilename}.rels`;
|
|
430
|
+
const relsFile = files.find(f => f.path === relsFilename);
|
|
431
|
+
const drawingMap = {}; // rId -> drawingPath
|
|
432
|
+
const sheetCommentsMap = {};
|
|
433
|
+
if (relsFile) {
|
|
434
|
+
const relsXml = (0, xmlUtils_js_1.parseXmlString)(relsFile.content.toString());
|
|
435
|
+
const relationships = (0, xmlUtils_js_1.getElementsByTagName)(relsXml, "Relationship");
|
|
436
|
+
for (const rel of relationships) {
|
|
437
|
+
const id = rel.getAttribute("Id");
|
|
438
|
+
const target = rel.getAttribute("Target");
|
|
439
|
+
const type = rel.getAttribute("Type");
|
|
440
|
+
if (id && target && type) {
|
|
441
|
+
if (config.extractAttachments && type.includes('drawing')) {
|
|
442
|
+
drawingMap[id] = 'xl/drawings/' + target.replace('../drawings/', '');
|
|
443
|
+
}
|
|
444
|
+
else if (!config.ignoreComments && type.includes('comments')) {
|
|
445
|
+
const commentsPath = 'xl/' + target.replace('../', '');
|
|
446
|
+
const cFile = files.find(f => f.path === commentsPath);
|
|
447
|
+
if (cFile) {
|
|
448
|
+
const cXml = (0, xmlUtils_js_1.parseXmlString)(cFile.content.toString());
|
|
449
|
+
const commentNodes = (0, xmlUtils_js_1.getElementsByTagName)(cXml, "comment");
|
|
450
|
+
const authorsList = (0, xmlUtils_js_1.getElementsByTagName)(cXml, "author");
|
|
451
|
+
const authors = authorsList.map(a => a.textContent || '');
|
|
452
|
+
for (const cNode of commentNodes) {
|
|
453
|
+
const ref = cNode.getAttribute("ref");
|
|
454
|
+
const authorId = cNode.getAttribute("authorId");
|
|
455
|
+
const author = authorId !== null ? authors[parseInt(authorId)] : undefined;
|
|
456
|
+
const tNodes = (0, xmlUtils_js_1.getElementsByTagName)(cNode, "t");
|
|
457
|
+
const text = tNodes.map(t => t.textContent || '').join('');
|
|
458
|
+
if (ref && text) {
|
|
459
|
+
if (!sheetCommentsMap[ref])
|
|
460
|
+
sheetCommentsMap[ref] = [];
|
|
461
|
+
sheetCommentsMap[ref].push({
|
|
462
|
+
type: 'comment',
|
|
463
|
+
text: text,
|
|
464
|
+
children: [{ type: 'text', text: text, formatting: {} }],
|
|
465
|
+
metadata: author ? { author } : undefined
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
const rows = [];
|
|
475
|
+
const sheetXml = file.content.toString();
|
|
476
|
+
// regex to match <row> elements, capturing:
|
|
477
|
+
// 1. attributes (e.g., r="1")
|
|
478
|
+
// 2. whether it's self-closing (/>)
|
|
479
|
+
// 3. inner content (for non-self-closing rows)
|
|
480
|
+
const rowRegex = /<row\b([^>]*?)(?:(\/>)|(>([\s\S]*?)<\/row>))/g;
|
|
481
|
+
// matchAll provides an iterator over all matches, which is much more efficient than
|
|
482
|
+
// iterating over a massive sparse row range declared in spreadsheet dimensions.
|
|
483
|
+
const rowMatches = sheetXml.matchAll(rowRegex);
|
|
484
|
+
/** Helper to convert Excel column string (A, B, AA, etc.) to 0-based index */
|
|
485
|
+
const colToNumber = (col) => {
|
|
486
|
+
let num = 0;
|
|
487
|
+
for (let i = 0; i < col.length; i++) {
|
|
488
|
+
num = num * 26 + (col.charCodeAt(i) - 'A'.charCodeAt(0) + 1);
|
|
489
|
+
}
|
|
490
|
+
return num - 1;
|
|
491
|
+
};
|
|
492
|
+
let lastRowIndex = -1;
|
|
493
|
+
for (const rowMatch of rowMatches) {
|
|
494
|
+
(0, errorUtils_js_1.checkAbortSignal)(config.abortSignal);
|
|
495
|
+
const rowXml = rowMatch[0];
|
|
496
|
+
const rowAttrs = rowMatch[1];
|
|
497
|
+
const isSelfClosing = !!rowMatch[2];
|
|
498
|
+
const rowContent = rowMatch[4] || "";
|
|
499
|
+
if (!isSelfClosing && !rowContent.includes('<c'))
|
|
500
|
+
continue;
|
|
501
|
+
const cells = [];
|
|
502
|
+
// regex to match <c> (cell) elements within a row, capturing:
|
|
503
|
+
// 1. cell attributes (e.g., r="A1", t="s")
|
|
504
|
+
// 2. whether it's self-closing (/>)
|
|
505
|
+
// 3. inner content (e.g., <v> value)
|
|
506
|
+
const cRegex = /<c\b([^>]*?)(?:(\/>)|(>([\s\S]*?)<\/c>))/g;
|
|
507
|
+
const cMatches = rowContent.matchAll(cRegex);
|
|
508
|
+
const rMatch = rowAttrs.match(/r="(\d+)"/);
|
|
509
|
+
const rowIndex = rMatch ? parseInt(rMatch[1]) - 1 : lastRowIndex + 1;
|
|
510
|
+
lastRowIndex = rowIndex;
|
|
511
|
+
let lastColIndex = -1;
|
|
512
|
+
for (const cMatch of cMatches) {
|
|
513
|
+
const cXml = cMatch[0];
|
|
514
|
+
const cAttrs = cMatch[1];
|
|
515
|
+
const cContent = cMatch[4] || "";
|
|
516
|
+
// Extract cell value
|
|
517
|
+
const typeMatch = cAttrs.match(/t="([a-zA-Z]+)"/);
|
|
518
|
+
const type = typeMatch ? typeMatch[1] : 'n'; // n = number (default)
|
|
519
|
+
const vMatch = cContent.match(/<v>([\s\S]*?)<\/v>/);
|
|
520
|
+
const tMatch = cContent.match(/<t\b[^>]*>([\s\S]*?)<\/t>/);
|
|
521
|
+
let text = '';
|
|
522
|
+
let cellNodes = [];
|
|
523
|
+
if (type === 's' && vMatch) {
|
|
524
|
+
const idx = parseInt(vMatch[1]);
|
|
525
|
+
const content = sharedStrings[idx];
|
|
526
|
+
if (Array.isArray(content)) {
|
|
527
|
+
// Rich text runs
|
|
528
|
+
// Deep copy runs to avoid reference issues if reused
|
|
529
|
+
cellNodes = JSON.parse(JSON.stringify(content));
|
|
530
|
+
text = cellNodes.map(n => n.text).join('');
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
text = content || '';
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
else if (type === 'inlineStr' && tMatch) {
|
|
537
|
+
text = (0, xmlUtils_js_1.decodeXmlEntities)(tMatch[1].trim());
|
|
538
|
+
}
|
|
539
|
+
else if (vMatch) {
|
|
540
|
+
text = vMatch[1].trim();
|
|
541
|
+
}
|
|
542
|
+
// Parse cell coordinate
|
|
543
|
+
const coordMatch = cAttrs.match(/r="([A-Z]+)(\d+)"/);
|
|
544
|
+
let colIndex;
|
|
545
|
+
let ref;
|
|
546
|
+
if (coordMatch) {
|
|
547
|
+
ref = coordMatch[1] + coordMatch[2];
|
|
548
|
+
colIndex = colToNumber(coordMatch[1]);
|
|
549
|
+
// If row index is missing in cell coord (unlikely but possible), use rowIndex
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
colIndex = lastColIndex + 1;
|
|
553
|
+
}
|
|
554
|
+
lastColIndex = colIndex;
|
|
555
|
+
if (text || cellNodes.length > 0) {
|
|
556
|
+
// Extract cell style index
|
|
557
|
+
const styleMatch = cAttrs.match(/s="(\d+)"/);
|
|
558
|
+
const styleIdx = styleMatch ? parseInt(styleMatch[1]) : undefined;
|
|
559
|
+
const cellFormatting = (styleIdx !== undefined && cellFormatMap[styleIdx]) ? cellFormatMap[styleIdx] : {};
|
|
560
|
+
if (cellNodes.length > 0) {
|
|
561
|
+
// If we have specific runs, merge cell styles into them if run style is missing
|
|
562
|
+
// But usually run style overrides cell style (except maybe background)
|
|
563
|
+
for (const node of cellNodes) {
|
|
564
|
+
if (!node.formatting)
|
|
565
|
+
node.formatting = {};
|
|
566
|
+
// Cell background always applies
|
|
567
|
+
if (cellFormatting.backgroundColor)
|
|
568
|
+
node.formatting.backgroundColor = cellFormatting.backgroundColor;
|
|
569
|
+
// Cell alignment always applies
|
|
570
|
+
if (cellFormatting.alignment)
|
|
571
|
+
node.formatting.alignment = cellFormatting.alignment;
|
|
572
|
+
// Font defaults from cell style if not in run
|
|
573
|
+
if (!node.formatting.font && cellFormatting.font)
|
|
574
|
+
node.formatting.font = cellFormatting.font;
|
|
575
|
+
if (!node.formatting.size && cellFormatting.size)
|
|
576
|
+
node.formatting.size = cellFormatting.size;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
// Simple text node
|
|
581
|
+
cellNodes.push({
|
|
582
|
+
type: 'text',
|
|
583
|
+
text: text,
|
|
584
|
+
formatting: cellFormatting
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
const commentsNodeList = (ref && sheetCommentsMap[ref]) ? sheetCommentsMap[ref] : undefined;
|
|
588
|
+
const cellNode = {
|
|
589
|
+
type: 'cell',
|
|
590
|
+
text: text,
|
|
591
|
+
children: cellNodes,
|
|
592
|
+
comments: commentsNodeList,
|
|
593
|
+
metadata: { row: rowIndex, col: colIndex }
|
|
594
|
+
};
|
|
595
|
+
if (config.includeRawContent) {
|
|
596
|
+
cellNode.rawContent = cXml;
|
|
597
|
+
}
|
|
598
|
+
cells.push(cellNode);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
if (cells.length > 0) {
|
|
602
|
+
const rowNode = {
|
|
603
|
+
type: 'row',
|
|
604
|
+
children: cells,
|
|
605
|
+
metadata: undefined
|
|
606
|
+
};
|
|
607
|
+
if (config.includeRawContent) {
|
|
608
|
+
rowNode.rawContent = rowXml;
|
|
609
|
+
}
|
|
610
|
+
rows.push(rowNode);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
// Handle Drawings in Sheet (images and charts)
|
|
614
|
+
if (config.extractAttachments) {
|
|
615
|
+
const drawingMatches = file.content.toString().match(/<drawing r:id="(.*?)"/g);
|
|
616
|
+
if (drawingMatches) {
|
|
617
|
+
for (const match of drawingMatches) {
|
|
618
|
+
const rIdMatch = match.match(/r:id="(.*?)"/);
|
|
619
|
+
const rId = rIdMatch ? rIdMatch[1] : null;
|
|
620
|
+
if (rId && drawingMap[rId]) {
|
|
621
|
+
const drawingPath = drawingMap[rId];
|
|
622
|
+
// Find all images in this drawing
|
|
623
|
+
const images = drawingImageMap[drawingPath];
|
|
624
|
+
if (images) {
|
|
625
|
+
for (const imgId in images) {
|
|
626
|
+
const imgInfo = images[imgId];
|
|
627
|
+
const attachment = attachments.find(a => a.name === imgInfo.path.split('/').pop());
|
|
628
|
+
if (attachment) {
|
|
629
|
+
const imageNode = {
|
|
630
|
+
type: 'image',
|
|
631
|
+
text: '', // Will be populated by assignAttachmentData
|
|
632
|
+
children: [],
|
|
633
|
+
metadata: {
|
|
634
|
+
attachmentName: attachment.name || 'unknown',
|
|
635
|
+
altText: imgInfo.altText || undefined
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
rows.push(imageNode);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
// Find all charts in this drawing
|
|
643
|
+
const charts = drawingChartMap[drawingPath];
|
|
644
|
+
if (charts) {
|
|
645
|
+
for (const chartRId in charts) {
|
|
646
|
+
const chartName = charts[chartRId];
|
|
647
|
+
const attachment = attachments.find(a => a.name === chartName);
|
|
648
|
+
if (attachment) {
|
|
649
|
+
const chartNode = {
|
|
650
|
+
type: 'chart',
|
|
651
|
+
text: '', // Will be populated by assignAttachmentData
|
|
652
|
+
children: [],
|
|
653
|
+
metadata: {
|
|
654
|
+
attachmentName: chartName
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
rows.push(chartNode);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
// Get proper sheet name from workbook.xml mapping, fallback to filename
|
|
666
|
+
const sheetFileName = file.path.split('/').pop() || 'Sheet';
|
|
667
|
+
const sheetName = sheetNameMap[sheetFileName] || sheetFileName;
|
|
668
|
+
content.push({
|
|
669
|
+
type: 'sheet',
|
|
670
|
+
children: rows,
|
|
671
|
+
metadata: { sheetName },
|
|
672
|
+
rawContent: config.includeRawContent ? file.content.toString() : undefined
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
const corePropsFile = files.find(f => f.path.match(corePropsFileRegex));
|
|
677
|
+
const metadata = corePropsFile ? (0, xmlUtils_js_1.parseOfficeMetadata)(corePropsFile.content.toString()) : {};
|
|
678
|
+
const customPropsFile = files.find(f => f.path.match(customPropsFileRegex));
|
|
679
|
+
if (customPropsFile) {
|
|
680
|
+
const customProperties = (0, xmlUtils_js_1.parseOOXMLCustomProperties)(customPropsFile.content.toString());
|
|
681
|
+
if (Object.keys(customProperties).length > 0)
|
|
682
|
+
metadata.customProperties = customProperties;
|
|
683
|
+
}
|
|
684
|
+
const appPropsFile = files.find(f => f.path.match(appPropsFileRegex));
|
|
685
|
+
if (appPropsFile) {
|
|
686
|
+
const appProperties = (0, xmlUtils_js_1.parseOOXMLAppProperties)(appPropsFile.content.toString());
|
|
687
|
+
if (Object.keys(appProperties).length > 0)
|
|
688
|
+
metadata.nativeProperties = appProperties;
|
|
689
|
+
}
|
|
690
|
+
// Link OCR text and chart data to content nodes (like PPTX parser)
|
|
691
|
+
const assignAttachmentData = (nodes) => {
|
|
692
|
+
for (const node of nodes) {
|
|
693
|
+
if ('attachmentName' in (node.metadata || {})) {
|
|
694
|
+
const meta = node.metadata;
|
|
695
|
+
const attachment = attachments.find(a => a.name === meta.attachmentName);
|
|
696
|
+
if (attachment) {
|
|
697
|
+
if (node.type === 'image') {
|
|
698
|
+
// Link OCR text to image node
|
|
699
|
+
if (attachment.ocrText) {
|
|
700
|
+
node.text = attachment.ocrText;
|
|
701
|
+
}
|
|
702
|
+
// Copy altText to attachment
|
|
703
|
+
if (meta.altText) {
|
|
704
|
+
attachment.altText = meta.altText;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
if (node.type === 'chart') {
|
|
708
|
+
// Link chart data text to chart node
|
|
709
|
+
if (attachment.chartData) {
|
|
710
|
+
node.text = attachment.chartData.rawTexts.join(config.newlineDelimiter);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
if (node.children) {
|
|
716
|
+
assignAttachmentData(node.children);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
assignAttachmentData(content);
|
|
721
|
+
const toTextSync = () => content.map(c => {
|
|
722
|
+
// Recursive text extraction
|
|
723
|
+
const getText = (node) => {
|
|
724
|
+
let t = '';
|
|
725
|
+
if (node.children) {
|
|
726
|
+
t += node.children.map(getText).filter(t => t != '').join(!node.children[0]?.children ? '' : config.newlineDelimiter);
|
|
727
|
+
}
|
|
728
|
+
else
|
|
729
|
+
t += node.text || '';
|
|
730
|
+
return t;
|
|
731
|
+
};
|
|
732
|
+
return getText(c);
|
|
733
|
+
}).filter(t => t != '').join(config.newlineDelimiter);
|
|
734
|
+
return (0, astUtils_js_1.createAST)('xlsx', metadata, content, attachments, config, undefined, toTextSync);
|
|
735
|
+
};
|
|
736
|
+
exports.parseExcel = parseExcel;
|