@webskill/sdk 0.27.1 → 0.29.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/dist/agent.d.ts +3 -3
- package/dist/agent.js +901 -120
- package/dist/{approval-BSwffz8P.js → approval-DnUytrkq.js} +7 -2
- package/dist/browser.d.ts +12 -12
- package/dist/browser.js +527 -69
- package/dist/{geometry-BXMPNuM6.js → geometry-QmrkJw1_.js} +13 -2
- package/dist/governance.d.ts +3 -3
- package/dist/{index--GvzV6Lq.d.ts → index-B5zHZDNA.d.ts} +91 -6
- package/dist/{index-C8-9-8zo.d.ts → index-C3t1jKAs.d.ts} +41 -13
- package/dist/{index-D6eSrEi5.d.ts → index-Cngzg2WS.d.ts} +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +12 -7
- package/dist/{linkedDocument-0iiVPd5t.js → linkedDocument-GE-VV_Gj.js} +112 -2
- package/dist/mcp.d.ts +2 -2
- package/dist/{miniChart-D0nYMzz8.js → miniChart-k95Dpy0q.js} +82 -4
- package/dist/node.d.ts +4 -3
- package/dist/node.js +19 -2
- package/dist/{openUiLibrary-CxtrnX54.js → openUiLibrary-BnhYUlqY.js} +1 -1
- package/dist/{openUiSpecLang-DIcLcybq.js → openUiSpecLang-C8qNOxzA.js} +432 -19
- package/dist/{renderResult-D9Q-Vu2x.js → renderResult-2v3WQzs_.js} +5 -2
- package/dist/{skillVersionStore-S4_HJ_Fl-Ds97pwm5.d.ts → skillVersionStore-S4_HJ_Fl-AiNsSSdO.d.ts} +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/{types-CpDRZ0rA-CFQ-vdKz.d.ts → types-wKp3YBSM-D7w-ZbsB.d.ts} +8 -3
- package/dist/ui-react.d.ts +3 -3
- package/dist/ui-react.js +5767 -793
- package/dist/ui-vue.d.ts +1 -1
- package/dist/ui-vue.js +1 -1
- package/dist/ui.d.ts +4 -4
- package/dist/ui.js +13 -7
- package/dist/{webSkillApi-C_7CTwtB.js → webSkillApi-TeWPPFDn.js} +1 -1
- package/dist/{webskillLitCatalog-BPyjYuRT.js → webskillLitCatalog-MfiLE5aq.js} +41 -2
- package/package.json +3 -1
- package/dist/kind-Dc8x0HWz.js +0 -110
package/dist/browser.js
CHANGED
|
@@ -2,14 +2,14 @@ import { n as messageOf$1, t as WebSkillError } from "./errors-BDZNpC13.js";
|
|
|
2
2
|
import { A as SKILL_MANIFEST_FILE, D as verifySkillSignature, M as buildManifest, O as MANIFEST_EXCLUDED_FILES, P as verifyManifest, b as parseSkillMarkdown, c as unzipWithLimits, f as SKILL_PACK_FILE, i as buildCatalog, k as SKILLS_LOCKFILE, m as parseSkillPackManifest, n as SkillDiscovery, o as readResponseWithLimit, p as exportSkills, t as validateSkills, u as detectSkillArchiveShapeFromFs, w as readSkillSignature, y as isValidSkillName } from "./skill-WSUDp6A1.js";
|
|
3
3
|
import { o as atomicWriteText, r as resolveInsideRoot, s as isAtomicTempPath } from "./pathSecurity-CirkQwWP.js";
|
|
4
4
|
import { t as assertRemoteUrlAllowed } from "./urlSafety-CiSuCJvX.js";
|
|
5
|
-
import {
|
|
6
|
-
import { _ as FsRunSnapshotStore, a as networkPolicyLibSource, c as bridgeError, ct as ProgressiveRouter, d as FsMemoryStore, l as parseBridgeRequest, m as AgentLoop, r as normalizeToolError, s as UPLOAD_FILES_UNAVAILABLE, t as CapabilityApproval, u as FsArtifactStore } from "./approval-
|
|
5
|
+
import { c as toBase64$1, f as SPREADSHEET_MIME_TYPE, y as ATTACHMENT_TEXT_LIMIT } from "./linkedDocument-GE-VV_Gj.js";
|
|
6
|
+
import { _ as FsRunSnapshotStore, a as networkPolicyLibSource, c as bridgeError, ct as ProgressiveRouter, d as FsMemoryStore, l as parseBridgeRequest, m as AgentLoop, r as normalizeToolError, s as UPLOAD_FILES_UNAVAILABLE, t as CapabilityApproval, u as FsArtifactStore } from "./approval-DnUytrkq.js";
|
|
7
7
|
import { a as GoogleGenAiClient, o as AnthropicClient, s as OpenAiCompatibleClient, t as MockLlmClient } from "./llm-eIQNO9tr.js";
|
|
8
|
-
import { c as toBase64$1, f as SPREADSHEET_MIME_TYPE } from "./linkedDocument-0iiVPd5t.js";
|
|
9
8
|
import { n as normalizeToolContent, t as mergeCatalogEntries } from "./external-_ZRQe-V9.js";
|
|
10
|
-
import { t as createWebSkillApi } from "./webSkillApi-
|
|
11
|
-
import { A as frameSteps, D as SCROLL_DIRECTIONS, E as toActionFrameScopes, O as toFrameScopes, _ as defersImage, a as columnWidthToPixels, b as isPdfTextTrustworthy, c as isRangeTopLeft, d as rangeContains, f as readImageSize, k as frameLabel, l as parseCellAddress, n as EMU_PER_POINT, o as fitWithin, p as PDF_LARGE_IMAGE_RATIO, r as EMU_PER_TWIP$1, s as formatCellAddress, t as EMU_PER_PIXEL, u as parseCellRange, v as takesImage } from "./geometry-
|
|
12
|
-
import { zipSync } from "fflate";
|
|
9
|
+
import { t as createWebSkillApi } from "./webSkillApi-TeWPPFDn.js";
|
|
10
|
+
import { A as frameSteps, D as SCROLL_DIRECTIONS, E as toActionFrameScopes, O as toFrameScopes, _ as defersImage, a as columnWidthToPixels, b as isPdfTextTrustworthy, c as isRangeTopLeft, d as rangeContains, f as readImageSize, k as frameLabel, l as parseCellAddress, n as EMU_PER_POINT, o as fitWithin, p as PDF_LARGE_IMAGE_RATIO, r as EMU_PER_TWIP$1, s as formatCellAddress, t as EMU_PER_PIXEL, u as parseCellRange, v as takesImage } from "./geometry-QmrkJw1_.js";
|
|
11
|
+
import { Unzip, UnzipInflate, zipSync } from "fflate";
|
|
12
|
+
import { SaxesParser } from "saxes";
|
|
13
13
|
|
|
14
14
|
//#region ../browser/src/fs/featureDetection.ts
|
|
15
15
|
/** 检测当前环境是否可用 OPFS(navigator.storage.getDirectory) */
|
|
@@ -98,6 +98,21 @@ var OpfsProvider = class {
|
|
|
98
98
|
return new Uint8Array(await (await handle.getFile()).arrayBuffer());
|
|
99
99
|
});
|
|
100
100
|
}
|
|
101
|
+
async createReadStream(p) {
|
|
102
|
+
const stream = (await this.#wrap(p, async () => (await this.#walkFile(p)).getFile())).stream();
|
|
103
|
+
return (async function* () {
|
|
104
|
+
const reader = stream.getReader();
|
|
105
|
+
try {
|
|
106
|
+
for (;;) {
|
|
107
|
+
const { done, value } = await reader.read();
|
|
108
|
+
if (done) return;
|
|
109
|
+
if (value !== void 0) yield value;
|
|
110
|
+
}
|
|
111
|
+
} finally {
|
|
112
|
+
reader.releaseLock();
|
|
113
|
+
}
|
|
114
|
+
})();
|
|
115
|
+
}
|
|
101
116
|
async writeBinary(p, content) {
|
|
102
117
|
await this.#wrap(p, async () => {
|
|
103
118
|
const segments = this.#segments(p);
|
|
@@ -6408,14 +6423,129 @@ async function extractDocxText(bytes) {
|
|
|
6408
6423
|
return blocks.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
6409
6424
|
}
|
|
6410
6425
|
|
|
6426
|
+
//#endregion
|
|
6427
|
+
//#region ../browser/src/document/xlsConversion.ts
|
|
6428
|
+
const OLE2_SIGNATURE = [
|
|
6429
|
+
208,
|
|
6430
|
+
207,
|
|
6431
|
+
17,
|
|
6432
|
+
224,
|
|
6433
|
+
161,
|
|
6434
|
+
177,
|
|
6435
|
+
26,
|
|
6436
|
+
225
|
|
6437
|
+
];
|
|
6438
|
+
function isLegacyXls(bytes) {
|
|
6439
|
+
return OLE2_SIGNATURE.every((byte, index) => bytes[index] === byte);
|
|
6440
|
+
}
|
|
6441
|
+
/** 旧 BIFF/OLE2 工作簿只在内存中转换;原件与上传落盘内容都不改写。 */
|
|
6442
|
+
async function convertXlsToXlsx(bytes) {
|
|
6443
|
+
if (!isLegacyXls(bytes)) throw new WebSkillError("TOOL_EXECUTION_FAILED", "The file is not a legacy OLE2 .xls workbook.");
|
|
6444
|
+
try {
|
|
6445
|
+
const [XLSX, codepage] = await Promise.all([import("@e965/xlsx"), import("@e965/xlsx/dist/cpexcel.full.mjs")]);
|
|
6446
|
+
const { cptable } = codepage;
|
|
6447
|
+
const { utils } = codepage;
|
|
6448
|
+
XLSX.set_cptable({
|
|
6449
|
+
...cptable,
|
|
6450
|
+
utils
|
|
6451
|
+
});
|
|
6452
|
+
const workbook = XLSX.read(bytes, {
|
|
6453
|
+
type: "array",
|
|
6454
|
+
cellDates: false,
|
|
6455
|
+
cellNF: true
|
|
6456
|
+
});
|
|
6457
|
+
const output = XLSX.write(workbook, {
|
|
6458
|
+
type: "array",
|
|
6459
|
+
bookType: "xlsx",
|
|
6460
|
+
compression: true
|
|
6461
|
+
});
|
|
6462
|
+
if (output instanceof Uint8Array) return output;
|
|
6463
|
+
if (output instanceof ArrayBuffer) return new Uint8Array(output);
|
|
6464
|
+
throw new Error("The converter returned an unsupported binary output.");
|
|
6465
|
+
} catch (error) {
|
|
6466
|
+
throw new WebSkillError("TOOL_EXECUTION_FAILED", `The legacy .xls workbook could not be converted to .xlsx: ${error instanceof Error ? error.message : String(error)}`, error);
|
|
6467
|
+
}
|
|
6468
|
+
}
|
|
6469
|
+
async function normalizeExcelBytes(bytes) {
|
|
6470
|
+
return isLegacyXls(bytes) ? convertXlsToXlsx(bytes) : bytes;
|
|
6471
|
+
}
|
|
6472
|
+
async function collect(chunks) {
|
|
6473
|
+
const parts = [];
|
|
6474
|
+
let length = 0;
|
|
6475
|
+
for await (const chunk of chunks) {
|
|
6476
|
+
parts.push(chunk);
|
|
6477
|
+
length += chunk.byteLength;
|
|
6478
|
+
}
|
|
6479
|
+
const bytes = new Uint8Array(length);
|
|
6480
|
+
let offset = 0;
|
|
6481
|
+
for (const part of parts) {
|
|
6482
|
+
bytes.set(part, offset);
|
|
6483
|
+
offset += part.byteLength;
|
|
6484
|
+
}
|
|
6485
|
+
return bytes;
|
|
6486
|
+
}
|
|
6487
|
+
/**
|
|
6488
|
+
* 流式读取先复用首段判断魔数。普通 xlsx 的首段会原样接回流,不整份缓存;
|
|
6489
|
+
* 只有必须整体转换的 xls 才收集全部字节。
|
|
6490
|
+
*/
|
|
6491
|
+
async function normalizeExcelSource(source) {
|
|
6492
|
+
if (typeof source !== "function") return normalizeExcelBytes(source);
|
|
6493
|
+
const iterator = (await source())[Symbol.asyncIterator]();
|
|
6494
|
+
const prefix = [];
|
|
6495
|
+
let prefixLength = 0;
|
|
6496
|
+
let done = false;
|
|
6497
|
+
while (prefixLength < OLE2_SIGNATURE.length && !done) {
|
|
6498
|
+
const next = await iterator.next();
|
|
6499
|
+
if (next.done === true) done = true;
|
|
6500
|
+
else {
|
|
6501
|
+
prefix.push(next.value);
|
|
6502
|
+
prefixLength += next.value.byteLength;
|
|
6503
|
+
}
|
|
6504
|
+
}
|
|
6505
|
+
const replay = async function* () {
|
|
6506
|
+
yield* prefix;
|
|
6507
|
+
try {
|
|
6508
|
+
while (!done) {
|
|
6509
|
+
const next = await iterator.next();
|
|
6510
|
+
if (next.done === true) {
|
|
6511
|
+
done = true;
|
|
6512
|
+
break;
|
|
6513
|
+
}
|
|
6514
|
+
yield next.value;
|
|
6515
|
+
}
|
|
6516
|
+
} finally {
|
|
6517
|
+
if (!done) await iterator.return?.();
|
|
6518
|
+
}
|
|
6519
|
+
};
|
|
6520
|
+
const head = new Uint8Array(Math.min(prefixLength, OLE2_SIGNATURE.length));
|
|
6521
|
+
let offset = 0;
|
|
6522
|
+
for (const chunk of prefix) {
|
|
6523
|
+
head.set(chunk.subarray(0, head.byteLength - offset), offset);
|
|
6524
|
+
offset = Math.min(head.byteLength, offset + chunk.byteLength);
|
|
6525
|
+
if (offset === head.byteLength) break;
|
|
6526
|
+
}
|
|
6527
|
+
if (!isLegacyXls(head)) {
|
|
6528
|
+
let pending = replay();
|
|
6529
|
+
return async () => {
|
|
6530
|
+
if (pending !== void 0) {
|
|
6531
|
+
const first = pending;
|
|
6532
|
+
pending = void 0;
|
|
6533
|
+
return first;
|
|
6534
|
+
}
|
|
6535
|
+
return await source();
|
|
6536
|
+
};
|
|
6537
|
+
}
|
|
6538
|
+
return convertXlsToXlsx(await collect(replay()));
|
|
6539
|
+
}
|
|
6540
|
+
|
|
6411
6541
|
//#endregion
|
|
6412
6542
|
//#region ../browser/src/document/xlsx.ts
|
|
6413
6543
|
/**
|
|
6414
6544
|
* xlsx 文本抽取(0.13.0 分册 12)。
|
|
6415
6545
|
*
|
|
6416
|
-
*
|
|
6417
|
-
*
|
|
6418
|
-
*
|
|
6546
|
+
* xlsx 和 docx 是同一种容器(OPC = zip + XML),所以沿用 docx 那条路 ——
|
|
6547
|
+
* zip 走仓内既有的 `unzipWithLimits`(fflate,自带解压体积上限,顺带挡住 zip 炸弹),
|
|
6548
|
+
* XML 走浏览器原生 `DOMParser`。旧版 xls 先由 `xlsConversion` 在内存中转成 xlsx。
|
|
6419
6549
|
*
|
|
6420
6550
|
* ⚠️ `DOMParser` 是浏览器 API,Node 没有 —— 所以这个模块只在 `@webskill/browser`。
|
|
6421
6551
|
* Node 宿主没有 xlsx 能力,与 docx 同一条**显式声明**的限制,不是静默降级(备案 D55)。
|
|
@@ -6437,21 +6567,10 @@ const XLSX_UNEXTRACTED = [
|
|
|
6437
6567
|
"hidden row and column marking",
|
|
6438
6568
|
"formula expressions (only cached values are read)"
|
|
6439
6569
|
];
|
|
6440
|
-
const WORKBOOK = "xl/workbook.xml";
|
|
6441
|
-
const WORKBOOK_RELS = "xl/_rels/workbook.xml.rels";
|
|
6442
|
-
const SHARED_STRINGS = "xl/sharedStrings.xml";
|
|
6443
|
-
const STYLES = "xl/styles.xml";
|
|
6444
|
-
/** OLE2 复合文档签名:旧版 .xls 的开头,它不是 zip,走不到解压那一步 */
|
|
6445
|
-
const OLE2_SIGNATURE = [
|
|
6446
|
-
208,
|
|
6447
|
-
207,
|
|
6448
|
-
17,
|
|
6449
|
-
224,
|
|
6450
|
-
161,
|
|
6451
|
-
177,
|
|
6452
|
-
26,
|
|
6453
|
-
225
|
|
6454
|
-
];
|
|
6570
|
+
const WORKBOOK$1 = "xl/workbook.xml";
|
|
6571
|
+
const WORKBOOK_RELS$1 = "xl/_rels/workbook.xml.rels";
|
|
6572
|
+
const SHARED_STRINGS$1 = "xl/sharedStrings.xml";
|
|
6573
|
+
const STYLES$1 = "xl/styles.xml";
|
|
6455
6574
|
/** 内建的日期/时间数字格式 id(ECMA-376 §18.8.30) */
|
|
6456
6575
|
const BUILTIN_DATE_FORMATS = /* @__PURE__ */ new Set([
|
|
6457
6576
|
14,
|
|
@@ -6467,14 +6586,14 @@ const BUILTIN_DATE_FORMATS = /* @__PURE__ */ new Set([
|
|
|
6467
6586
|
46,
|
|
6468
6587
|
47
|
|
6469
6588
|
]);
|
|
6470
|
-
function fail(message) {
|
|
6589
|
+
function fail$1(message) {
|
|
6471
6590
|
throw new WebSkillError("TOOL_EXECUTION_FAILED", message);
|
|
6472
6591
|
}
|
|
6473
6592
|
/** 按 localName 取子孙元素,忽略命名空间前缀:不同生成器的前缀写法并不统一 */
|
|
6474
|
-
const byTag$
|
|
6475
|
-
function parseXml$
|
|
6593
|
+
const byTag$4 = (scope, name) => [...scope.getElementsByTagNameNS("*", name)];
|
|
6594
|
+
function parseXml$4(bytes, what) {
|
|
6476
6595
|
const doc = new DOMParser().parseFromString(new TextDecoder().decode(bytes), "application/xml");
|
|
6477
|
-
if (doc.getElementsByTagName("parsererror").length > 0) fail(`The workbook ${what} could not be parsed as XML.`);
|
|
6596
|
+
if (doc.getElementsByTagName("parsererror").length > 0) fail$1(`The workbook ${what} could not be parsed as XML.`);
|
|
6478
6597
|
return doc;
|
|
6479
6598
|
}
|
|
6480
6599
|
/** `B` → 1、`AA` → 26;拿不到列号时返回 undefined,由调用方顺位补上 */
|
|
@@ -6519,14 +6638,14 @@ function isDateFormatCode(code) {
|
|
|
6519
6638
|
/** 单元格样式索引 → 是否日期格式 */
|
|
6520
6639
|
function readDateStyles(bytes) {
|
|
6521
6640
|
if (bytes === void 0) return [];
|
|
6522
|
-
const doc = parseXml$
|
|
6641
|
+
const doc = parseXml$4(bytes, "styles");
|
|
6523
6642
|
const custom = /* @__PURE__ */ new Map();
|
|
6524
|
-
for (const fmt of byTag$
|
|
6643
|
+
for (const fmt of byTag$4(doc, "numFmt")) {
|
|
6525
6644
|
const id = Number(fmt.getAttribute("numFmtId"));
|
|
6526
6645
|
const code = fmt.getAttribute("formatCode");
|
|
6527
6646
|
if (Number.isFinite(id) && code !== null) custom.set(id, code);
|
|
6528
6647
|
}
|
|
6529
|
-
const cellXfs = byTag$
|
|
6648
|
+
const cellXfs = byTag$4(doc, "cellXfs")[0];
|
|
6530
6649
|
if (cellXfs === void 0) return [];
|
|
6531
6650
|
return [...cellXfs.children].filter((xf) => xf.localName === "xf").map((xf) => {
|
|
6532
6651
|
const id = Number(xf.getAttribute("numFmtId") ?? "0");
|
|
@@ -6591,7 +6710,7 @@ function cellText$1(cell, shared, dateStyles, date1904) {
|
|
|
6591
6710
|
function sheetRows(doc, shared, dateStyles, date1904) {
|
|
6592
6711
|
const rows = [];
|
|
6593
6712
|
let previousRow = 0;
|
|
6594
|
-
for (const row of byTag$
|
|
6713
|
+
for (const row of byTag$4(doc, "row")) {
|
|
6595
6714
|
const declared = Number(row.getAttribute("r"));
|
|
6596
6715
|
const rowNumber = Number.isInteger(declared) && declared > 0 ? declared : previousRow + 1;
|
|
6597
6716
|
for (let gap = previousRow + 1; gap < rowNumber; gap++) rows.push([]);
|
|
@@ -6614,49 +6733,48 @@ function sheetRows(doc, shared, dateStyles, date1904) {
|
|
|
6614
6733
|
}
|
|
6615
6734
|
/** 一行的文本:单元格用制表符分隔(与 docx 的表格同口径),行尾制表符不留 */
|
|
6616
6735
|
const rowLine = (cells) => cells.join(" ").replace(/\t+$/, "");
|
|
6617
|
-
const looksLikeOle2 = (bytes) => bytes.length >= OLE2_SIGNATURE.length && OLE2_SIGNATURE.every((byte, i) => bytes[i] === byte);
|
|
6618
6736
|
/** rels 里的 Target 可能是相对 `xl/` 的,也可能是包根绝对路径 */
|
|
6619
|
-
function resolveSheetPath(target) {
|
|
6737
|
+
function resolveSheetPath$1(target) {
|
|
6620
6738
|
const clean = target.replace(/^\.\//, "");
|
|
6621
6739
|
return clean.startsWith("/") ? clean.slice(1) : `xl/${clean}`;
|
|
6622
6740
|
}
|
|
6623
6741
|
/**
|
|
6624
6742
|
* 把 xlsx 拆成「每张表的每一行文本」。
|
|
6625
6743
|
*
|
|
6626
|
-
* @throws WebSkillError 归档损坏、旧版 `.xls
|
|
6744
|
+
* @throws WebSkillError 归档损坏、旧版 `.xls` 转换失败、缺 `xl/workbook.xml`、工作表关系解不开、XML 解析失败
|
|
6627
6745
|
*/
|
|
6628
6746
|
async function readXlsxWorkbook(bytes) {
|
|
6629
|
-
|
|
6747
|
+
const normalized = await normalizeExcelBytes(bytes);
|
|
6630
6748
|
let entries;
|
|
6631
6749
|
try {
|
|
6632
|
-
entries = await unzipWithLimits(
|
|
6750
|
+
entries = await unzipWithLimits(normalized);
|
|
6633
6751
|
} catch {
|
|
6634
|
-
fail("This file could not be read as an Excel workbook: the archive is damaged or not a .xlsx file.");
|
|
6752
|
+
fail$1("This file could not be read as an Excel workbook: the archive is damaged or not a .xlsx file.");
|
|
6635
6753
|
}
|
|
6636
6754
|
const parts = new Map(entries);
|
|
6637
|
-
const workbookBytes = parts.get(WORKBOOK);
|
|
6638
|
-
if (workbookBytes === void 0) fail(`This file is not an Excel workbook: it has no ${WORKBOOK} entry.`);
|
|
6639
|
-
const workbook = parseXml$
|
|
6640
|
-
const workbookPr = byTag$
|
|
6755
|
+
const workbookBytes = parts.get(WORKBOOK$1);
|
|
6756
|
+
if (workbookBytes === void 0) fail$1(`This file is not an Excel workbook: it has no ${WORKBOOK$1} entry.`);
|
|
6757
|
+
const workbook = parseXml$4(workbookBytes, "index");
|
|
6758
|
+
const workbookPr = byTag$4(workbook, "workbookPr")[0];
|
|
6641
6759
|
const date1904 = workbookPr?.getAttribute("date1904") === "1" || workbookPr?.getAttribute("date1904") === "true";
|
|
6642
|
-
const relsBytes = parts.get(WORKBOOK_RELS);
|
|
6760
|
+
const relsBytes = parts.get(WORKBOOK_RELS$1);
|
|
6643
6761
|
const targets = /* @__PURE__ */ new Map();
|
|
6644
|
-
if (relsBytes !== void 0) for (const rel of byTag$
|
|
6762
|
+
if (relsBytes !== void 0) for (const rel of byTag$4(parseXml$4(relsBytes, "relationships"), "Relationship")) {
|
|
6645
6763
|
const id = rel.getAttribute("Id");
|
|
6646
6764
|
const target = rel.getAttribute("Target");
|
|
6647
|
-
if (id !== null && target !== null) targets.set(id, resolveSheetPath(target));
|
|
6765
|
+
if (id !== null && target !== null) targets.set(id, resolveSheetPath$1(target));
|
|
6648
6766
|
}
|
|
6649
|
-
const sharedBytes = parts.get(SHARED_STRINGS);
|
|
6650
|
-
const shared = sharedBytes === void 0 ? [] : byTag$
|
|
6651
|
-
const dateStyles = readDateStyles(parts.get(STYLES));
|
|
6767
|
+
const sharedBytes = parts.get(SHARED_STRINGS$1);
|
|
6768
|
+
const shared = sharedBytes === void 0 ? [] : byTag$4(parseXml$4(sharedBytes, "shared strings"), "si").map(sharedStringText);
|
|
6769
|
+
const dateStyles = readDateStyles(parts.get(STYLES$1));
|
|
6652
6770
|
const sheets = [];
|
|
6653
|
-
for (const [ordinal, sheet] of byTag$
|
|
6771
|
+
for (const [ordinal, sheet] of byTag$4(workbook, "sheet").entries()) {
|
|
6654
6772
|
const name = sheet.getAttribute("name") ?? `Sheet${ordinal + 1}`;
|
|
6655
6773
|
const relId = sheet.getAttributeNS("http://schemas.openxmlformats.org/officeDocument/2006/relationships", "id") ?? sheet.getAttribute("r:id");
|
|
6656
6774
|
const path = relId !== null ? targets.get(relId) : void 0;
|
|
6657
6775
|
const sheetBytes = path !== void 0 ? parts.get(path) : void 0;
|
|
6658
|
-
if (path === void 0 || sheetBytes === void 0) fail(`The worksheet "${name}" could not be located inside the workbook.`);
|
|
6659
|
-
const rows = sheetRows(parseXml$
|
|
6776
|
+
if (path === void 0 || sheetBytes === void 0) fail$1(`The worksheet "${name}" could not be located inside the workbook.`);
|
|
6777
|
+
const rows = sheetRows(parseXml$4(sheetBytes, `worksheet "${name}"`), shared, dateStyles, date1904);
|
|
6660
6778
|
const lines = rows.map(rowLine);
|
|
6661
6779
|
while (lines.length > 0 && lines[lines.length - 1] === "") {
|
|
6662
6780
|
lines.pop();
|
|
@@ -6677,7 +6795,7 @@ async function readXlsxWorkbook(bytes) {
|
|
|
6677
6795
|
/**
|
|
6678
6796
|
* 从 xlsx 字节里抽出文本,每个工作表一段、段首是表名。
|
|
6679
6797
|
*
|
|
6680
|
-
* @throws WebSkillError 归档损坏、旧版 `.xls
|
|
6798
|
+
* @throws WebSkillError 归档损坏、旧版 `.xls` 转换失败、缺 `xl/workbook.xml`、工作表关系解不开、XML 解析失败
|
|
6681
6799
|
*/
|
|
6682
6800
|
async function extractXlsxText(bytes) {
|
|
6683
6801
|
const { sheets } = await readXlsxWorkbook(bytes);
|
|
@@ -6738,7 +6856,7 @@ const DEFAULT_BODY_TWIPS = 9360;
|
|
|
6738
6856
|
function attr(node, name) {
|
|
6739
6857
|
for (const item of node.attributes) if (item.name === name || item.localName === name) return item.value;
|
|
6740
6858
|
}
|
|
6741
|
-
function parseXml$
|
|
6859
|
+
function parseXml$3(bytes, what) {
|
|
6742
6860
|
const doc = new DOMParser().parseFromString(new TextDecoder().decode(bytes), "application/xml");
|
|
6743
6861
|
if (doc.getElementsByTagName("parsererror").length > 0) throw new WebSkillError("TOOL_EXECUTION_FAILED", `The Word document's ${what} could not be parsed as XML.`);
|
|
6744
6862
|
return doc;
|
|
@@ -6801,10 +6919,10 @@ async function readDocxBlocks(bytes) {
|
|
|
6801
6919
|
const files = new Map(entries);
|
|
6802
6920
|
const main = files.get(DOCUMENT$1);
|
|
6803
6921
|
if (main === void 0) throw new WebSkillError("TOOL_EXECUTION_FAILED", `This file is not a Word document: it has no ${DOCUMENT$1} entry.`);
|
|
6804
|
-
const doc = parseXml$
|
|
6922
|
+
const doc = parseXml$3(main, "body");
|
|
6805
6923
|
const targets = /* @__PURE__ */ new Map();
|
|
6806
6924
|
const rels = files.get(RELS);
|
|
6807
|
-
if (rels !== void 0) for (const rel of parseXml$
|
|
6925
|
+
if (rels !== void 0) for (const rel of parseXml$3(rels, "relationships").getElementsByTagName("Relationship")) {
|
|
6808
6926
|
const id = attr(rel, "Id");
|
|
6809
6927
|
const target = attr(rel, "Target");
|
|
6810
6928
|
if (id !== void 0 && target !== void 0) targets.set(id, `word/${target.replace(/^\.?\//, "")}`);
|
|
@@ -6853,8 +6971,8 @@ const ANCHOR_TAGS = /* @__PURE__ */ new Set([
|
|
|
6853
6971
|
/** 绝对锚点没有行号;放在表尾,保证图片不丢。 */
|
|
6854
6972
|
const AFTER_LAST_ROW = Number.MAX_SAFE_INTEGER;
|
|
6855
6973
|
const RELATIONSHIPS_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
6856
|
-
const byTag$
|
|
6857
|
-
function parseXml$
|
|
6974
|
+
const byTag$3 = (scope, name) => [...scope.getElementsByTagNameNS("*", name)];
|
|
6975
|
+
function parseXml$2(bytes) {
|
|
6858
6976
|
const doc = new DOMParser().parseFromString(new TextDecoder().decode(bytes), "application/xml");
|
|
6859
6977
|
return doc.getElementsByTagName("parsererror").length > 0 ? void 0 : doc;
|
|
6860
6978
|
}
|
|
@@ -6875,9 +6993,9 @@ const relsOf = (path) => `${dirOf(path)}_rels/${path.slice(path.lastIndexOf("/")
|
|
|
6875
6993
|
function readRels(parts, path) {
|
|
6876
6994
|
const out = /* @__PURE__ */ new Map();
|
|
6877
6995
|
const bytes = parts.get(relsOf(path));
|
|
6878
|
-
const doc = bytes === void 0 ? void 0 : parseXml$
|
|
6996
|
+
const doc = bytes === void 0 ? void 0 : parseXml$2(bytes);
|
|
6879
6997
|
if (doc === void 0) return out;
|
|
6880
|
-
for (const rel of byTag$
|
|
6998
|
+
for (const rel of byTag$3(doc, "Relationship")) {
|
|
6881
6999
|
const id = rel.getAttribute("Id");
|
|
6882
7000
|
const target = rel.getAttribute("Target");
|
|
6883
7001
|
if (id !== null && target !== null) out.set(id, resolveRelative$1(dirOf(path), target));
|
|
@@ -6885,7 +7003,7 @@ function readRels(parts, path) {
|
|
|
6885
7003
|
return out;
|
|
6886
7004
|
}
|
|
6887
7005
|
const anchorNumber = (anchor, name) => {
|
|
6888
|
-
const node = anchor === void 0 ? void 0 : byTag$
|
|
7006
|
+
const node = anchor === void 0 ? void 0 : byTag$3(anchor, name)[0];
|
|
6889
7007
|
const value = Number(node?.textContent ?? NaN);
|
|
6890
7008
|
return Number.isInteger(value) && value >= 0 ? value : 0;
|
|
6891
7009
|
};
|
|
@@ -6902,14 +7020,14 @@ function imagesOf(parts, sheetPath) {
|
|
|
6902
7020
|
for (const [, target] of sheetRels) {
|
|
6903
7021
|
if (!target.includes("/drawings/")) continue;
|
|
6904
7022
|
const drawingBytes = parts.get(target);
|
|
6905
|
-
const drawing = drawingBytes === void 0 ? void 0 : parseXml$
|
|
7023
|
+
const drawing = drawingBytes === void 0 ? void 0 : parseXml$2(drawingBytes);
|
|
6906
7024
|
if (drawing === void 0) continue;
|
|
6907
7025
|
const drawingRels = readRels(parts, target);
|
|
6908
7026
|
const anchors = [...drawing.documentElement?.children ?? []].filter((element) => ANCHOR_TAGS.has(element.localName));
|
|
6909
7027
|
for (const anchor of anchors) {
|
|
6910
|
-
const from = byTag$
|
|
7028
|
+
const from = byTag$3(anchor, "from")[0];
|
|
6911
7029
|
const row = anchor.localName === "absoluteAnchor" || from === void 0 ? AFTER_LAST_ROW : anchorNumber(from, "row");
|
|
6912
|
-
const blip = byTag$
|
|
7030
|
+
const blip = byTag$3(anchor, "blip")[0];
|
|
6913
7031
|
const relId = blip?.getAttributeNS(RELATIONSHIPS_NS, "embed") ?? blip?.getAttribute("r:embed");
|
|
6914
7032
|
const path = relId === null || relId === void 0 ? void 0 : drawingRels.get(relId);
|
|
6915
7033
|
const data = path === void 0 ? void 0 : parts.get(path);
|
|
@@ -6926,7 +7044,7 @@ function createXlsxBlockReader() {
|
|
|
6926
7044
|
return { read: readXlsxBlocks };
|
|
6927
7045
|
}
|
|
6928
7046
|
/**
|
|
6929
|
-
* @throws WebSkillError 归档损坏、旧版 `.xls
|
|
7047
|
+
* @throws WebSkillError 归档损坏、旧版 `.xls` 转换失败、缺 `xl/workbook.xml`、工作表关系解不开、XML 解析失败
|
|
6930
7048
|
* @experimental
|
|
6931
7049
|
*/
|
|
6932
7050
|
async function readXlsxBlocks(bytes) {
|
|
@@ -6951,18 +7069,358 @@ async function readXlsxBlocks(bytes) {
|
|
|
6951
7069
|
return { sheets: out };
|
|
6952
7070
|
}
|
|
6953
7071
|
|
|
7072
|
+
//#endregion
|
|
7073
|
+
//#region ../browser/src/document/xlsxStreaming.ts
|
|
7074
|
+
const WORKBOOK = "xl/workbook.xml";
|
|
7075
|
+
const WORKBOOK_RELS = "xl/_rels/workbook.xml.rels";
|
|
7076
|
+
const SHARED_STRINGS = "xl/sharedStrings.xml";
|
|
7077
|
+
const STYLES = "xl/styles.xml";
|
|
7078
|
+
function fail(message) {
|
|
7079
|
+
throw new WebSkillError("TOOL_EXECUTION_FAILED", message);
|
|
7080
|
+
}
|
|
7081
|
+
async function sourceChunks(source) {
|
|
7082
|
+
if (typeof source !== "function") return (async function* () {
|
|
7083
|
+
yield source;
|
|
7084
|
+
})();
|
|
7085
|
+
return await source();
|
|
7086
|
+
}
|
|
7087
|
+
async function* entryChunks(source, path, state) {
|
|
7088
|
+
const output = [];
|
|
7089
|
+
let failure;
|
|
7090
|
+
const unzip = new Unzip();
|
|
7091
|
+
unzip.register(UnzipInflate);
|
|
7092
|
+
unzip.onfile = (file) => {
|
|
7093
|
+
if (file.name !== path) return;
|
|
7094
|
+
state.found = true;
|
|
7095
|
+
file.ondata = (error, chunk) => {
|
|
7096
|
+
if (error !== null) failure = error;
|
|
7097
|
+
else if (failure === void 0) output.push(chunk);
|
|
7098
|
+
};
|
|
7099
|
+
file.start();
|
|
7100
|
+
};
|
|
7101
|
+
try {
|
|
7102
|
+
const chunks = await sourceChunks(source);
|
|
7103
|
+
for await (const chunk of chunks) {
|
|
7104
|
+
unzip.push(chunk, false);
|
|
7105
|
+
yield* output.splice(0);
|
|
7106
|
+
await Promise.resolve();
|
|
7107
|
+
yield* output.splice(0);
|
|
7108
|
+
}
|
|
7109
|
+
unzip.push(/* @__PURE__ */ new Uint8Array(), true);
|
|
7110
|
+
await Promise.resolve();
|
|
7111
|
+
yield* output.splice(0);
|
|
7112
|
+
} catch (e) {
|
|
7113
|
+
failure ??= e;
|
|
7114
|
+
}
|
|
7115
|
+
if (failure !== void 0) {
|
|
7116
|
+
if (failure instanceof WebSkillError) throw failure;
|
|
7117
|
+
throw new WebSkillError("TOOL_EXECUTION_FAILED", `The workbook entry "${path}" could not be read: ${failure instanceof Error ? failure.message : String(failure)}`, failure);
|
|
7118
|
+
}
|
|
7119
|
+
}
|
|
7120
|
+
async function entryBytes(source, path) {
|
|
7121
|
+
const chunks = [];
|
|
7122
|
+
let total = 0;
|
|
7123
|
+
const state = { found: false };
|
|
7124
|
+
for await (const chunk of entryChunks(source, path, state)) {
|
|
7125
|
+
chunks.push(chunk);
|
|
7126
|
+
total += chunk.byteLength;
|
|
7127
|
+
}
|
|
7128
|
+
if (!state.found) return void 0;
|
|
7129
|
+
const out = new Uint8Array(total);
|
|
7130
|
+
let offset = 0;
|
|
7131
|
+
for (const chunk of chunks) {
|
|
7132
|
+
out.set(chunk, offset);
|
|
7133
|
+
offset += chunk.byteLength;
|
|
7134
|
+
}
|
|
7135
|
+
return out;
|
|
7136
|
+
}
|
|
7137
|
+
function parseXml$1(bytes, what) {
|
|
7138
|
+
const doc = new DOMParser().parseFromString(new TextDecoder().decode(bytes), "application/xml");
|
|
7139
|
+
if (doc.getElementsByTagName("parsererror").length > 0) fail(`The workbook ${what} could not be parsed as XML.`);
|
|
7140
|
+
return doc;
|
|
7141
|
+
}
|
|
7142
|
+
const byTag$2 = (scope, name) => [...scope.getElementsByTagNameNS("*", name)];
|
|
7143
|
+
const localName = (value) => {
|
|
7144
|
+
const name = typeof value === "string" ? value : value.name;
|
|
7145
|
+
return name.slice(name.lastIndexOf(":") + 1);
|
|
7146
|
+
};
|
|
7147
|
+
function attribute(tag, name) {
|
|
7148
|
+
const direct = tag.attributes[name];
|
|
7149
|
+
if (direct !== void 0) return direct;
|
|
7150
|
+
const key = Object.keys(tag.attributes).find((candidate) => candidate.endsWith(`:${name}`));
|
|
7151
|
+
return key === void 0 ? null : tag.attributes[key] ?? null;
|
|
7152
|
+
}
|
|
7153
|
+
function newParser(onError) {
|
|
7154
|
+
const parser = new SaxesParser();
|
|
7155
|
+
parser.on("error", onError);
|
|
7156
|
+
return parser;
|
|
7157
|
+
}
|
|
7158
|
+
async function readSharedStrings(source) {
|
|
7159
|
+
const shared = [];
|
|
7160
|
+
let inside = false;
|
|
7161
|
+
let textDepth = 0;
|
|
7162
|
+
let phoneticDepth = 0;
|
|
7163
|
+
let text = "";
|
|
7164
|
+
let parseError;
|
|
7165
|
+
const parser = newParser((error) => {
|
|
7166
|
+
parseError = error;
|
|
7167
|
+
});
|
|
7168
|
+
parser.on("opentag", (tag) => {
|
|
7169
|
+
const name = localName(tag.name);
|
|
7170
|
+
if (name === "si") {
|
|
7171
|
+
inside = true;
|
|
7172
|
+
text = "";
|
|
7173
|
+
} else if (inside && name === "rPh") phoneticDepth += 1;
|
|
7174
|
+
else if (inside && name === "t" && phoneticDepth === 0) textDepth += 1;
|
|
7175
|
+
});
|
|
7176
|
+
parser.on("text", (value) => {
|
|
7177
|
+
if (inside && textDepth > 0 && phoneticDepth === 0) text += value;
|
|
7178
|
+
});
|
|
7179
|
+
parser.on("cdata", (value) => {
|
|
7180
|
+
if (inside && textDepth > 0 && phoneticDepth === 0) text += value;
|
|
7181
|
+
});
|
|
7182
|
+
parser.on("closetag", (nameValue) => {
|
|
7183
|
+
const name = localName(nameValue);
|
|
7184
|
+
if (name === "t" && textDepth > 0) textDepth -= 1;
|
|
7185
|
+
else if (name === "rPh" && phoneticDepth > 0) phoneticDepth -= 1;
|
|
7186
|
+
else if (name === "si" && inside) {
|
|
7187
|
+
shared.push(text);
|
|
7188
|
+
inside = false;
|
|
7189
|
+
text = "";
|
|
7190
|
+
}
|
|
7191
|
+
});
|
|
7192
|
+
const state = { found: false };
|
|
7193
|
+
const decoder = new TextDecoder();
|
|
7194
|
+
for await (const chunk of entryChunks(source, SHARED_STRINGS, state)) parser.write(decoder.decode(chunk, { stream: true }));
|
|
7195
|
+
if (!state.found) return [];
|
|
7196
|
+
parser.write(decoder.decode());
|
|
7197
|
+
parser.close();
|
|
7198
|
+
if (parseError !== void 0) fail(`The workbook shared strings could not be parsed as XML: ${parseError.message}`);
|
|
7199
|
+
return shared;
|
|
7200
|
+
}
|
|
7201
|
+
function scanWorksheet(source, path, shared, dateStyles, date1904) {
|
|
7202
|
+
return (async function* () {
|
|
7203
|
+
const queue = [];
|
|
7204
|
+
let previousRow = 0;
|
|
7205
|
+
let lastYielded = 0;
|
|
7206
|
+
let rowNumber = 0;
|
|
7207
|
+
let cells = [];
|
|
7208
|
+
let nextColumn = 0;
|
|
7209
|
+
let cell;
|
|
7210
|
+
let parseError;
|
|
7211
|
+
const parser = newParser((error) => {
|
|
7212
|
+
parseError = error;
|
|
7213
|
+
});
|
|
7214
|
+
const drain = async function* () {
|
|
7215
|
+
for (const entry of queue.splice(0)) {
|
|
7216
|
+
for (let gap = lastYielded + 1; gap < entry.row; gap += 1) yield {
|
|
7217
|
+
row: gap,
|
|
7218
|
+
cells: []
|
|
7219
|
+
};
|
|
7220
|
+
lastYielded = entry.row;
|
|
7221
|
+
yield entry;
|
|
7222
|
+
}
|
|
7223
|
+
};
|
|
7224
|
+
parser.on("opentag", (tag) => {
|
|
7225
|
+
const name = localName(tag.name);
|
|
7226
|
+
if (name === "row") {
|
|
7227
|
+
const declared = Number(attribute(tag, "r"));
|
|
7228
|
+
rowNumber = Number.isInteger(declared) && declared > 0 ? declared : previousRow + 1;
|
|
7229
|
+
previousRow = rowNumber;
|
|
7230
|
+
cells = [];
|
|
7231
|
+
nextColumn = 0;
|
|
7232
|
+
return;
|
|
7233
|
+
}
|
|
7234
|
+
if (name === "c") {
|
|
7235
|
+
const column = columnIndex(attribute(tag, "r")) ?? nextColumn;
|
|
7236
|
+
cell = {
|
|
7237
|
+
type: attribute(tag, "t"),
|
|
7238
|
+
style: Number(attribute(tag, "s") ?? "0"),
|
|
7239
|
+
column,
|
|
7240
|
+
raw: "",
|
|
7241
|
+
inline: "",
|
|
7242
|
+
valueDepth: 0,
|
|
7243
|
+
inlineDepth: 0,
|
|
7244
|
+
textDepth: 0,
|
|
7245
|
+
phoneticDepth: 0
|
|
7246
|
+
};
|
|
7247
|
+
nextColumn = column + 1;
|
|
7248
|
+
} else if (cell !== void 0 && name === "v") cell.valueDepth += 1;
|
|
7249
|
+
else if (cell !== void 0 && name === "is") cell.inlineDepth += 1;
|
|
7250
|
+
else if (cell !== void 0 && name === "rPh" && cell.inlineDepth > 0) cell.phoneticDepth += 1;
|
|
7251
|
+
else if (cell !== void 0 && name === "t" && cell.inlineDepth > 0 && cell.phoneticDepth === 0) cell.textDepth += 1;
|
|
7252
|
+
});
|
|
7253
|
+
parser.on("text", (value) => {
|
|
7254
|
+
if (cell === void 0) return;
|
|
7255
|
+
if (cell.valueDepth > 0) cell.raw += value;
|
|
7256
|
+
if (cell.textDepth > 0 && cell.phoneticDepth === 0) cell.inline += value;
|
|
7257
|
+
});
|
|
7258
|
+
parser.on("cdata", (value) => {
|
|
7259
|
+
if (cell === void 0) return;
|
|
7260
|
+
if (cell.valueDepth > 0) cell.raw += value;
|
|
7261
|
+
if (cell.textDepth > 0 && cell.phoneticDepth === 0) cell.inline += value;
|
|
7262
|
+
});
|
|
7263
|
+
parser.on("closetag", (nameValue) => {
|
|
7264
|
+
const name = localName(nameValue);
|
|
7265
|
+
if (cell !== void 0 && name === "t" && cell.textDepth > 0) cell.textDepth -= 1;
|
|
7266
|
+
else if (cell !== void 0 && name === "rPh" && cell.phoneticDepth > 0) cell.phoneticDepth -= 1;
|
|
7267
|
+
else if (cell !== void 0 && name === "is" && cell.inlineDepth > 0) cell.inlineDepth -= 1;
|
|
7268
|
+
else if (cell !== void 0 && name === "v" && cell.valueDepth > 0) cell.valueDepth -= 1;
|
|
7269
|
+
else if (name === "c" && cell !== void 0) {
|
|
7270
|
+
let text = cell.raw;
|
|
7271
|
+
if (cell.type === "inlineStr") text = cell.inline;
|
|
7272
|
+
else if (cell.type === "s") {
|
|
7273
|
+
const index = Number(cell.raw);
|
|
7274
|
+
text = Number.isInteger(index) ? shared[index] ?? "" : "";
|
|
7275
|
+
} else if (cell.type === "b") text = cell.raw === "1" ? "TRUE" : "FALSE";
|
|
7276
|
+
else if (cell.type !== "str" && cell.type !== "e" && cell.raw !== "") {
|
|
7277
|
+
const numeric = Number(cell.raw);
|
|
7278
|
+
if (Number.isFinite(numeric) && Number.isInteger(cell.style) && dateStyles[cell.style] === true) text = serialToIso(numeric, date1904);
|
|
7279
|
+
}
|
|
7280
|
+
for (let gap = cells.length; gap < cell.column; gap += 1) cells.push("");
|
|
7281
|
+
if (cells.length > cell.column) cells[cell.column] = text;
|
|
7282
|
+
else cells.push(text);
|
|
7283
|
+
cell = void 0;
|
|
7284
|
+
} else if (name === "row") {
|
|
7285
|
+
while (cells.length > 0 && cells[cells.length - 1] === "") cells.pop();
|
|
7286
|
+
if (cells.length > 0) queue.push({
|
|
7287
|
+
row: rowNumber,
|
|
7288
|
+
cells
|
|
7289
|
+
});
|
|
7290
|
+
cells = [];
|
|
7291
|
+
}
|
|
7292
|
+
});
|
|
7293
|
+
const state = { found: false };
|
|
7294
|
+
const decoder = new TextDecoder();
|
|
7295
|
+
for await (const chunk of entryChunks(source, path, state)) {
|
|
7296
|
+
parser.write(decoder.decode(chunk, { stream: true }));
|
|
7297
|
+
yield* drain();
|
|
7298
|
+
}
|
|
7299
|
+
if (!state.found) fail(`The worksheet entry "${path}" could not be located inside the workbook.`);
|
|
7300
|
+
parser.write(decoder.decode());
|
|
7301
|
+
parser.close();
|
|
7302
|
+
if (parseError !== void 0) fail(`The worksheet "${path}" could not be parsed as XML: ${parseError.message}`);
|
|
7303
|
+
yield* drain();
|
|
7304
|
+
})();
|
|
7305
|
+
}
|
|
7306
|
+
function blank(cells) {
|
|
7307
|
+
return cells.every((value) => value.trim() === "");
|
|
7308
|
+
}
|
|
7309
|
+
function wide(code) {
|
|
7310
|
+
return code >= 19968 && code <= 40959 || code >= 13312 && code <= 19903 || code >= 12288 && code <= 12543 || code >= 44032 && code <= 55215 || code >= 63744 && code <= 64255 || code >= 65280 && code <= 65519;
|
|
7311
|
+
}
|
|
7312
|
+
const MAX_CACHED_ROWS = 5e4;
|
|
7313
|
+
const MAX_CACHED_CELLS = 5e5;
|
|
7314
|
+
async function measure(sheet) {
|
|
7315
|
+
let rowCount = 0;
|
|
7316
|
+
let columnCount = 0;
|
|
7317
|
+
let firstNonEmptyRow = 0;
|
|
7318
|
+
let firstRow = [];
|
|
7319
|
+
let dataRows = 0;
|
|
7320
|
+
let wideChars = 0;
|
|
7321
|
+
let otherChars = sheet.name.length + 8;
|
|
7322
|
+
let cellCount = 0;
|
|
7323
|
+
const rows = [];
|
|
7324
|
+
for await (const entry of sheet.scan()) {
|
|
7325
|
+
rowCount += 1;
|
|
7326
|
+
cellCount += entry.cells.length;
|
|
7327
|
+
if (rowCount <= MAX_CACHED_ROWS && cellCount <= MAX_CACHED_CELLS) rows.push(entry);
|
|
7328
|
+
columnCount = Math.max(columnCount, entry.cells.length);
|
|
7329
|
+
otherChars += Math.max(entry.cells.length - 1, 0) + 1;
|
|
7330
|
+
for (const value of entry.cells) for (let index = 0; index < value.length; index += 1) if (wide(value.charCodeAt(index))) wideChars += 1;
|
|
7331
|
+
else otherChars += 1;
|
|
7332
|
+
if (blank(entry.cells)) continue;
|
|
7333
|
+
if (firstNonEmptyRow === 0) {
|
|
7334
|
+
firstNonEmptyRow = entry.row;
|
|
7335
|
+
firstRow = entry.cells;
|
|
7336
|
+
} else dataRows += 1;
|
|
7337
|
+
}
|
|
7338
|
+
return {
|
|
7339
|
+
meta: {
|
|
7340
|
+
rowCount,
|
|
7341
|
+
columnCount,
|
|
7342
|
+
firstNonEmptyRow,
|
|
7343
|
+
firstRow,
|
|
7344
|
+
dataRows,
|
|
7345
|
+
estimatedTokens: wideChars + Math.ceil(otherChars / 4),
|
|
7346
|
+
textChars: wideChars + otherChars
|
|
7347
|
+
},
|
|
7348
|
+
...rowCount <= MAX_CACHED_ROWS && cellCount <= MAX_CACHED_CELLS ? { rows } : {}
|
|
7349
|
+
};
|
|
7350
|
+
}
|
|
7351
|
+
function resolveSheetPath(target) {
|
|
7352
|
+
const clean = target.replace(/^\.\//, "");
|
|
7353
|
+
return clean.startsWith("/") ? clean.slice(1) : `xl/${clean}`;
|
|
7354
|
+
}
|
|
7355
|
+
async function readStreamingXlsxWorkbook(input) {
|
|
7356
|
+
const source = await normalizeExcelSource(input);
|
|
7357
|
+
const workbookBytes = await entryBytes(source, WORKBOOK);
|
|
7358
|
+
if (workbookBytes === void 0) fail(`This file is not an Excel workbook: it has no ${WORKBOOK} entry.`);
|
|
7359
|
+
const workbook = parseXml$1(workbookBytes, "index");
|
|
7360
|
+
const workbookPr = byTag$2(workbook, "workbookPr")[0];
|
|
7361
|
+
const date1904 = workbookPr?.getAttribute("date1904") === "1" || workbookPr?.getAttribute("date1904") === "true";
|
|
7362
|
+
const relsBytes = await entryBytes(source, WORKBOOK_RELS);
|
|
7363
|
+
const targets = /* @__PURE__ */ new Map();
|
|
7364
|
+
if (relsBytes !== void 0) for (const rel of byTag$2(parseXml$1(relsBytes, "relationships"), "Relationship")) {
|
|
7365
|
+
const id = rel.getAttribute("Id");
|
|
7366
|
+
const target = rel.getAttribute("Target");
|
|
7367
|
+
if (id !== null && target !== null) targets.set(id, resolveSheetPath(target));
|
|
7368
|
+
}
|
|
7369
|
+
const [shared, dateStyles] = await Promise.all([readSharedStrings(source), entryBytes(source, STYLES).then((bytes) => readDateStyles(bytes))]);
|
|
7370
|
+
const sheets = [];
|
|
7371
|
+
for (const [ordinal, sheet] of byTag$2(workbook, "sheet").entries()) {
|
|
7372
|
+
const name = sheet.getAttribute("name") ?? `Sheet${ordinal + 1}`;
|
|
7373
|
+
const relId = sheet.getAttributeNS("http://schemas.openxmlformats.org/officeDocument/2006/relationships", "id") ?? sheet.getAttribute("r:id");
|
|
7374
|
+
const path = relId !== null ? targets.get(relId) : void 0;
|
|
7375
|
+
if (path === void 0) fail(`The worksheet "${name}" could not be located inside the workbook.`);
|
|
7376
|
+
let metadata;
|
|
7377
|
+
let cachedRows;
|
|
7378
|
+
const scan = () => {
|
|
7379
|
+
const rows = cachedRows;
|
|
7380
|
+
if (rows !== void 0) return (async function* () {
|
|
7381
|
+
yield* rows;
|
|
7382
|
+
})();
|
|
7383
|
+
return scanWorksheet(source, path, shared, dateStyles, date1904);
|
|
7384
|
+
};
|
|
7385
|
+
const entry = {
|
|
7386
|
+
name,
|
|
7387
|
+
scan,
|
|
7388
|
+
meta: () => {
|
|
7389
|
+
metadata ??= measure({
|
|
7390
|
+
name,
|
|
7391
|
+
scan
|
|
7392
|
+
}).then((result) => {
|
|
7393
|
+
cachedRows = result.rows;
|
|
7394
|
+
return result.meta;
|
|
7395
|
+
}).catch((error) => {
|
|
7396
|
+
metadata = void 0;
|
|
7397
|
+
throw error;
|
|
7398
|
+
});
|
|
7399
|
+
return metadata;
|
|
7400
|
+
}
|
|
7401
|
+
};
|
|
7402
|
+
sheets.push(entry);
|
|
7403
|
+
}
|
|
7404
|
+
return {
|
|
7405
|
+
kind: "streaming",
|
|
7406
|
+
sheets
|
|
7407
|
+
};
|
|
7408
|
+
}
|
|
7409
|
+
|
|
6954
7410
|
//#endregion
|
|
6955
7411
|
//#region ../browser/src/document/xlsxTable.ts
|
|
6956
7412
|
/**
|
|
6957
|
-
*
|
|
7413
|
+
* xls(x) → 单元格网格或可重复扫描的行流;旧版 xls 先在内存中转换成 xlsx。
|
|
6958
7414
|
*
|
|
6959
|
-
*
|
|
6960
|
-
*
|
|
6961
|
-
* 错误语义同 `extractXlsxText`:旧版 `.xls`、归档损坏、缺 `xl/workbook.xml` 都抛 `WebSkillError`。
|
|
7415
|
+
* `read` 保留既有网格契约;`readStreaming` 用 SAX 逐行解析工作表,不受整包解压的 64 MiB 条目上限约束。
|
|
7416
|
+
* 两条路径使用同一套单元格取值、日期换算与空格补位规则,错误均带 `WebSkillError`。
|
|
6962
7417
|
* @experimental
|
|
6963
7418
|
*/
|
|
6964
7419
|
function createXlsxTableReader() {
|
|
6965
|
-
return {
|
|
7420
|
+
return {
|
|
7421
|
+
read: readXlsxTable,
|
|
7422
|
+
readStreaming: readStreamingXlsxWorkbook
|
|
7423
|
+
};
|
|
6966
7424
|
}
|
|
6967
7425
|
async function readXlsxTable(bytes) {
|
|
6968
7426
|
const { sheets } = await readXlsxWorkbook(bytes);
|