@cj-tech-master/excelts 9.5.7 → 9.5.8-canary.20260528224225.faabb36
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/browser/modules/excel/utils/col-cache.js +19 -1
- package/dist/browser/modules/excel/workbook.browser.js +10 -0
- package/dist/browser/modules/excel/xlsx/xform/book/sheet-xform.d.ts +12 -2
- package/dist/browser/modules/excel/xlsx/xform/book/sheet-xform.js +56 -2
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-xform.d.ts +7 -0
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-xform.js +453 -30
- package/dist/cjs/modules/excel/utils/col-cache.js +19 -1
- package/dist/cjs/modules/excel/workbook.browser.js +10 -0
- package/dist/cjs/modules/excel/xlsx/xform/book/sheet-xform.js +56 -2
- package/dist/cjs/modules/excel/xlsx/xform/book/workbook-xform.js +453 -30
- package/dist/esm/modules/excel/utils/col-cache.js +19 -1
- package/dist/esm/modules/excel/workbook.browser.js +10 -0
- package/dist/esm/modules/excel/xlsx/xform/book/sheet-xform.js +56 -2
- package/dist/esm/modules/excel/xlsx/xform/book/workbook-xform.js +453 -30
- package/dist/iife/excelts.iife.js +365 -62
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +38 -38
- package/dist/types/modules/excel/xlsx/xform/book/sheet-xform.d.ts +12 -2
- package/dist/types/modules/excel/xlsx/xform/book/workbook-xform.d.ts +7 -0
- package/package.json +12 -12
|
@@ -51,7 +51,15 @@ const colCache = {
|
|
|
51
51
|
let l3;
|
|
52
52
|
let n = 1;
|
|
53
53
|
if (level >= 4) {
|
|
54
|
-
|
|
54
|
+
// Defensive invariant: Excel's column space (XFD = 16,384) caps at
|
|
55
|
+
// three letters, so neither `l2n` nor `n2l` should ever ask for a
|
|
56
|
+
// higher level. Both callers validate before reaching here; if
|
|
57
|
+
// this branch fires it indicates a programming error in a future
|
|
58
|
+
// caller, not a user input problem — surface that clearly rather
|
|
59
|
+
// than reusing `ColumnOutOfBoundsError` (which would lie about
|
|
60
|
+
// the offending column, since `level` is a letter-count, not a
|
|
61
|
+
// column number).
|
|
62
|
+
throw new Error(`colCache._fill: invariant violated — level ${level} exceeds the 3-letter cap; callers must validate before invoking _fill`);
|
|
55
63
|
}
|
|
56
64
|
if (this._l2nFill < 1 && level >= 1) {
|
|
57
65
|
while (n <= 26) {
|
|
@@ -92,6 +100,16 @@ const colCache = {
|
|
|
92
100
|
},
|
|
93
101
|
l2n(l) {
|
|
94
102
|
if (!this._l2n[l]) {
|
|
103
|
+
// Excel's column space stops at XFD (16,384) — three letters is
|
|
104
|
+
// the maximum width any valid column letter can have. Reject
|
|
105
|
+
// longer inputs explicitly here, BEFORE handing the length to
|
|
106
|
+
// `_fill`, so the thrown error carries the actual offending
|
|
107
|
+
// letter (`AAAA`) rather than the level integer (`4`) — matching
|
|
108
|
+
// what the equivalent `n2l(n > 16384)` and `decodeAddress` paths
|
|
109
|
+
// already report.
|
|
110
|
+
if (l.length > 3) {
|
|
111
|
+
throw new ColumnOutOfBoundsError(l, "Excel supports columns from 1 to 16384");
|
|
112
|
+
}
|
|
95
113
|
this._fill(l.length);
|
|
96
114
|
}
|
|
97
115
|
if (!this._l2n[l]) {
|
|
@@ -2289,6 +2289,16 @@ class Workbook {
|
|
|
2289
2289
|
this._tableNames.clear();
|
|
2290
2290
|
value.worksheets.forEach(worksheetModel => {
|
|
2291
2291
|
const { id, name, state } = worksheetModel;
|
|
2292
|
+
// API invariant: `_worksheets` is keyed by a positive integer
|
|
2293
|
+
// sheet id. A worksheet model with a missing or non-integer id
|
|
2294
|
+
// would be stored under a string pseudo key like `"undefined"`
|
|
2295
|
+
// or `"NaN"`, making it unreachable via `getWorksheet(name)`
|
|
2296
|
+
// (issue #166). The xlsx reconciler enforces the same invariant
|
|
2297
|
+
// before reaching this point; programmatic callers assigning
|
|
2298
|
+
// `model` directly with a malformed payload land here instead.
|
|
2299
|
+
if (!Number.isInteger(id) || id <= 0) {
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2292
2302
|
const orderNo = value.sheets && value.sheets.findIndex(ws => ws.id === id);
|
|
2293
2303
|
const worksheet = (this._worksheets[id] = new Worksheet({
|
|
2294
2304
|
id,
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import type { WorksheetState } from "../../../types.js";
|
|
2
2
|
import { BaseXform } from "../base-xform.js";
|
|
3
3
|
interface SheetModel {
|
|
4
|
-
id: number;
|
|
4
|
+
id: number | undefined;
|
|
5
5
|
name: string;
|
|
6
6
|
state: WorksheetState;
|
|
7
|
-
rId: string;
|
|
7
|
+
rId: string | undefined;
|
|
8
8
|
}
|
|
9
9
|
declare class WorksheetXform extends BaseXform {
|
|
10
|
+
relationshipsPrefixes: readonly string[];
|
|
10
11
|
render(xmlStream: any, model: SheetModel): void;
|
|
11
12
|
parseOpen(node: any): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Locate the relationship id on a `<sheet>` element. Tries every
|
|
15
|
+
* prefix the workbook root bound to the relationships namespace,
|
|
16
|
+
* then any prefix the `<sheet>` element itself rebinds locally.
|
|
17
|
+
* Returns `undefined` if no relationship id is present — callers
|
|
18
|
+
* (the workbook reconciler) will treat such a `<sheet>` as a
|
|
19
|
+
* half-broken declaration that can't be bound to a worksheet part.
|
|
20
|
+
*/
|
|
21
|
+
private _extractRelId;
|
|
12
22
|
parseText(): void;
|
|
13
23
|
parseClose(): boolean;
|
|
14
24
|
}
|
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
import { BaseXform } from "../base-xform.js";
|
|
2
2
|
const VALID_STATES = new Set(["visible", "hidden", "veryHidden"]);
|
|
3
|
+
const RELATIONSHIPS_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
3
4
|
function parseWorksheetState(raw) {
|
|
4
5
|
const state = raw || "visible";
|
|
5
6
|
return VALID_STATES.has(state) ? state : "visible";
|
|
6
7
|
}
|
|
8
|
+
function parseSheetId(raw) {
|
|
9
|
+
if (raw === undefined) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
const id = parseInt(raw, 10);
|
|
13
|
+
// OOXML constrains `sheetId` to a positive integer. Anything that
|
|
14
|
+
// doesn't parse to one — empty string, alphabetic, zero, negative,
|
|
15
|
+
// overflowing — must not propagate as `NaN`/`0`/`-1` because those
|
|
16
|
+
// would later seed pseudo keys like `_worksheets["NaN"]` (the same
|
|
17
|
+
// family of bug as issue #166's `_worksheets["undefined"]`).
|
|
18
|
+
return Number.isInteger(id) && id > 0 ? id : undefined;
|
|
19
|
+
}
|
|
7
20
|
class WorksheetXform extends BaseXform {
|
|
21
|
+
constructor() {
|
|
22
|
+
super(...arguments);
|
|
23
|
+
// All prefixes the workbook root binds to the OOXML relationships
|
|
24
|
+
// namespace. Conventionally a workbook declares `xmlns:r=…`, so the
|
|
25
|
+
// sheet uses `r:id="rId1"`. But the prefix is only a label and a
|
|
26
|
+
// workbook may legally bind any prefix (or several) to that
|
|
27
|
+
// namespace. `WorkbookXform` populates this list from the
|
|
28
|
+
// `<workbook>` root; `r` is the safe fallback when the workbook
|
|
29
|
+
// declares no relationships binding at all.
|
|
30
|
+
this.relationshipsPrefixes = ["r"];
|
|
31
|
+
}
|
|
8
32
|
render(xmlStream, model) {
|
|
9
33
|
xmlStream.leafNode("sheet", {
|
|
10
34
|
name: model.name,
|
|
@@ -18,14 +42,44 @@ class WorksheetXform extends BaseXform {
|
|
|
18
42
|
if (node.name === "sheet") {
|
|
19
43
|
this.model = {
|
|
20
44
|
name: node.attributes.name,
|
|
21
|
-
id:
|
|
45
|
+
id: parseSheetId(node.attributes.sheetId),
|
|
22
46
|
state: parseWorksheetState(node.attributes.state),
|
|
23
|
-
rId: node
|
|
47
|
+
rId: this._extractRelId(node)
|
|
24
48
|
};
|
|
25
49
|
return true;
|
|
26
50
|
}
|
|
27
51
|
return false;
|
|
28
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Locate the relationship id on a `<sheet>` element. Tries every
|
|
55
|
+
* prefix the workbook root bound to the relationships namespace,
|
|
56
|
+
* then any prefix the `<sheet>` element itself rebinds locally.
|
|
57
|
+
* Returns `undefined` if no relationship id is present — callers
|
|
58
|
+
* (the workbook reconciler) will treat such a `<sheet>` as a
|
|
59
|
+
* half-broken declaration that can't be bound to a worksheet part.
|
|
60
|
+
*/
|
|
61
|
+
_extractRelId(node) {
|
|
62
|
+
const attrs = node.attributes ?? {};
|
|
63
|
+
for (const prefix of this.relationshipsPrefixes) {
|
|
64
|
+
const value = attrs[`${prefix}:id`];
|
|
65
|
+
if (value !== undefined) {
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Local-scope fallback: a `<sheet>` element occasionally redeclares
|
|
70
|
+
// the relationships namespace under a fresh prefix. Scan its own
|
|
71
|
+
// attributes for `xmlns:X="…/relationships"` and look up `X:id`.
|
|
72
|
+
for (const attrName of Object.keys(attrs)) {
|
|
73
|
+
if (attrName.startsWith("xmlns:") && attrs[attrName] === RELATIONSHIPS_NS) {
|
|
74
|
+
const localPrefix = attrName.slice("xmlns:".length);
|
|
75
|
+
const candidate = attrs[`${localPrefix}:id`];
|
|
76
|
+
if (candidate !== undefined) {
|
|
77
|
+
return candidate;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
29
83
|
parseText() { }
|
|
30
84
|
parseClose() {
|
|
31
85
|
return false;
|
|
@@ -5,10 +5,17 @@ declare class WorkbookXform extends BaseXform {
|
|
|
5
5
|
map: {
|
|
6
6
|
[key: string]: any;
|
|
7
7
|
};
|
|
8
|
+
/**
|
|
9
|
+
* The `<sheet>` xform shared with the `sheets` ListXform. Held as a
|
|
10
|
+
* field so `parseOpen` can pass workbook-level state (the prefixes
|
|
11
|
+
* bound to the OOXML relationships namespace) into it.
|
|
12
|
+
*/
|
|
13
|
+
private readonly _sheetXform;
|
|
8
14
|
constructor();
|
|
9
15
|
prepare(model: any): void;
|
|
10
16
|
render(xmlStream: any, model: any): void;
|
|
11
17
|
parseOpen(node: any): boolean;
|
|
18
|
+
private static _findRelationshipsPrefixes;
|
|
12
19
|
parseText(text: string): void;
|
|
13
20
|
parseClose(name: string): boolean;
|
|
14
21
|
reconcile(model: any): void;
|