@bilig/xlsx 0.164.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 +25 -0
- package/dist/address.d.ts +15 -0
- package/dist/address.js +82 -0
- package/dist/address.js.map +1 -0
- package/dist/external-workbook-types.d.ts +44 -0
- package/dist/external-workbook-types.js +12 -0
- package/dist/external-workbook-types.js.map +1 -0
- package/dist/file-source.d.ts +10 -0
- package/dist/file-source.js +76 -0
- package/dist/file-source.js.map +1 -0
- package/dist/formula-cache-reader.d.ts +30 -0
- package/dist/formula-cache-reader.js +350 -0
- package/dist/formula-cache-reader.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/simple-workbook-writer.d.ts +114 -0
- package/dist/simple-workbook-writer.js +621 -0
- package/dist/simple-workbook-writer.js.map +1 -0
- package/dist/source-preserving-literal-patches.d.ts +43 -0
- package/dist/source-preserving-literal-patches.js +615 -0
- package/dist/source-preserving-literal-patches.js.map +1 -0
- package/dist/source-preserving-zip.d.ts +25 -0
- package/dist/source-preserving-zip.js +341 -0
- package/dist/source-preserving-zip.js.map +1 -0
- package/dist/streaming-native-cell-arena.d.ts +57 -0
- package/dist/streaming-native-cell-arena.js +233 -0
- package/dist/streaming-native-cell-arena.js.map +1 -0
- package/dist/streaming-native-external-cache.d.ts +13 -0
- package/dist/streaming-native-external-cache.js +753 -0
- package/dist/streaming-native-external-cache.js.map +1 -0
- package/dist/streaming-native-inspect.d.ts +42 -0
- package/dist/streaming-native-inspect.js +297 -0
- package/dist/streaming-native-inspect.js.map +1 -0
- package/dist/streaming-native-lookup-wasm.d.ts +13 -0
- package/dist/streaming-native-lookup-wasm.js +250 -0
- package/dist/streaming-native-lookup-wasm.js.map +1 -0
- package/dist/streaming-native-recalc-evaluator.d.ts +17 -0
- package/dist/streaming-native-recalc-evaluator.js +743 -0
- package/dist/streaming-native-recalc-evaluator.js.map +1 -0
- package/dist/streaming-native-recalc.d.ts +97 -0
- package/dist/streaming-native-recalc.js +652 -0
- package/dist/streaming-native-recalc.js.map +1 -0
- package/dist/streaming-native-row-chain-conditionals.d.ts +8 -0
- package/dist/streaming-native-row-chain-conditionals.js +385 -0
- package/dist/streaming-native-row-chain-conditionals.js.map +1 -0
- package/dist/streaming-native-row-chain-dependencies.d.ts +17 -0
- package/dist/streaming-native-row-chain-dependencies.js +365 -0
- package/dist/streaming-native-row-chain-dependencies.js.map +1 -0
- package/dist/streaming-native-row-chain-references.d.ts +3 -0
- package/dist/streaming-native-row-chain-references.js +36 -0
- package/dist/streaming-native-row-chain-references.js.map +1 -0
- package/dist/streaming-native-row-chain-wasm.d.ts +56 -0
- package/dist/streaming-native-row-chain-wasm.js +546 -0
- package/dist/streaming-native-row-chain-wasm.js.map +1 -0
- package/dist/streaming-native-text.d.ts +2 -0
- package/dist/streaming-native-text.js +14 -0
- package/dist/streaming-native-text.js.map +1 -0
- package/dist/streaming-native-workbook-core.d.ts +47 -0
- package/dist/streaming-native-workbook-core.js +110 -0
- package/dist/streaming-native-workbook-core.js.map +1 -0
- package/dist/targeted-cell-reader.d.ts +11 -0
- package/dist/targeted-cell-reader.js +92 -0
- package/dist/targeted-cell-reader.js.map +1 -0
- package/dist/workbook-cell-reader.d.ts +29 -0
- package/dist/workbook-cell-reader.js +200 -0
- package/dist/workbook-cell-reader.js.map +1 -0
- package/dist/workbook-compatibility-report.d.ts +101 -0
- package/dist/workbook-compatibility-report.js +654 -0
- package/dist/workbook-compatibility-report.js.map +1 -0
- package/dist/workbook-sheet-paths.d.ts +8 -0
- package/dist/workbook-sheet-paths.js +79 -0
- package/dist/workbook-sheet-paths.js.map +1 -0
- package/dist/xml-part-patch.d.ts +12 -0
- package/dist/xml-part-patch.js +45 -0
- package/dist/xml-part-patch.js.map +1 -0
- package/dist/xml.d.ts +9 -0
- package/dist/xml.js +42 -0
- package/dist/xml.js.map +1 -0
- package/dist/zip-reader.d.ts +51 -0
- package/dist/zip-reader.js +448 -0
- package/dist/zip-reader.js.map +1 -0
- package/package.json +113 -0
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
import { ErrorCode, formatErrorCode, ValueTag } from '@bilig/protocol';
|
|
2
|
+
import { decodeCellAddress, encodeCellAddress } from './address.js';
|
|
3
|
+
import { escapeXmlAttribute, escapeXmlText, readXmlAttribute } from './xml.js';
|
|
4
|
+
import { workbookSheetPathEntriesForSource } from './workbook-sheet-paths.js';
|
|
5
|
+
import { getZipText, normalizeZipPath, readXlsxZipEntries } from './zip-reader.js';
|
|
6
|
+
const externalReferencePattern = /'\[([1-9][0-9]*)\]((?:[^']|'')+)'!((?:\$?[A-Za-z]{1,3}\$?[0-9]+)(?::(?:\$?[A-Za-z]{1,3}\$?[0-9]+))?)|\[([1-9][0-9]*)\]([A-Za-z_][A-Za-z0-9_.]*)!((?:\$?[A-Za-z]{1,3}\$?[0-9]+)(?::(?:\$?[A-Za-z]{1,3}\$?[0-9]+))?)/gu;
|
|
7
|
+
const relationshipPattern = /<Relationship\b(?:[^>"']|"[^"]*"|'[^']*')*\/?>/gu;
|
|
8
|
+
const externalLinkRelationshipType = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLink';
|
|
9
|
+
const externalWorkbookCompanionNoMatchWarning = 'Some supplied external workbook companions could not be matched; existing external-link cache values were preserved.';
|
|
10
|
+
const externalWorkbookCompanionAmbiguousMatchWarning = 'Some supplied external workbook companions matched ambiguously; existing external-link cache values were preserved.';
|
|
11
|
+
const emptyCellValue = Object.freeze({ tag: ValueTag.Empty });
|
|
12
|
+
export function normalizeExternalWorkbookReferences(formula) {
|
|
13
|
+
externalReferencePattern.lastIndex = 0;
|
|
14
|
+
return formula.replace(externalReferencePattern, (_match, quotedBookIndex, quotedSheetName, quotedRef, unquotedBookIndex, unquotedSheetName, unquotedRef) => {
|
|
15
|
+
const bookIndex = Number(quotedBookIndex ?? unquotedBookIndex);
|
|
16
|
+
const sheetName = quotedSheetName ? quotedSheetName.replaceAll("''", "'") : (unquotedSheetName ?? '');
|
|
17
|
+
const ref = quotedRef ?? unquotedRef ?? '';
|
|
18
|
+
return `${quoteFormulaSheetName(externalReferenceAlias(bookIndex, sheetName))}!${ref}`;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export function readStreamingNativeExternalCachedRowsByAlias(zip, sheetScans, resolveFormulaSource, externalWorkbooks = undefined) {
|
|
22
|
+
const targetsByAlias = collectExternalReferenceScanTargets(sheetScans, resolveFormulaSource);
|
|
23
|
+
if (targetsByAlias.size === 0) {
|
|
24
|
+
return { rowsByAlias: new Map(), textPatches: [], warnings: [] };
|
|
25
|
+
}
|
|
26
|
+
const externalLinkReferencesByBookIndex = readExternalLinkReferencesByBookIndex(zip);
|
|
27
|
+
const targetsByBookIndex = new Map();
|
|
28
|
+
for (const target of targetsByAlias.values()) {
|
|
29
|
+
const targets = targetsByBookIndex.get(target.bookIndex) ?? [];
|
|
30
|
+
targets.push(target);
|
|
31
|
+
targetsByBookIndex.set(target.bookIndex, targets);
|
|
32
|
+
}
|
|
33
|
+
const output = new Map();
|
|
34
|
+
for (const [bookIndex, targets] of targetsByBookIndex.entries()) {
|
|
35
|
+
const reference = externalLinkReferencesByBookIndex.get(bookIndex);
|
|
36
|
+
if (!reference) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const externalLinkXml = getZipText(zip, reference.path);
|
|
40
|
+
if (!externalLinkXml) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
for (const [alias, rows] of parseExternalLinkCachedRows(bookIndex, externalLinkXml, targets).entries()) {
|
|
44
|
+
output.set(alias, rows);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (externalWorkbooks && externalWorkbooks.length > 0) {
|
|
48
|
+
const companionRows = readCompanionExternalCachedRowsByAlias(externalWorkbooks, externalLinkReferencesByBookIndex, targetsByBookIndex);
|
|
49
|
+
for (const [alias, rows] of companionRows.rowsByAlias.entries()) {
|
|
50
|
+
output.set(alias, rows);
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
rowsByAlias: output,
|
|
54
|
+
textPatches: companionRows.textPatches,
|
|
55
|
+
warnings: companionRows.warnings,
|
|
56
|
+
...(companionRows.diagnostics === undefined ? {} : { diagnostics: companionRows.diagnostics }),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return { rowsByAlias: output, textPatches: [], warnings: [] };
|
|
60
|
+
}
|
|
61
|
+
export function isStreamingNativeExternalReferenceAlias(sheetName) {
|
|
62
|
+
return sheetName.startsWith('__bilig_external_');
|
|
63
|
+
}
|
|
64
|
+
function collectExternalReferenceScanTargets(sheetScans, resolveFormulaSource) {
|
|
65
|
+
const targets = new Map();
|
|
66
|
+
for (const scan of sheetScans.values()) {
|
|
67
|
+
for (const cell of scan.formulaCells) {
|
|
68
|
+
let formula;
|
|
69
|
+
try {
|
|
70
|
+
formula = resolveFormulaSource(scan, cell);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
for (const reference of extractExternalFormulaReferences(formula)) {
|
|
76
|
+
const alias = externalReferenceAlias(reference.bookIndex, reference.sheetName);
|
|
77
|
+
const target = targets.get(alias) ?? {
|
|
78
|
+
bookIndex: reference.bookIndex,
|
|
79
|
+
sheetName: reference.sheetName,
|
|
80
|
+
cellsByRow: new Map(),
|
|
81
|
+
};
|
|
82
|
+
for (let row = reference.startRow; row <= reference.endRow; row += 1) {
|
|
83
|
+
const cols = target.cellsByRow.get(row) ?? new Set();
|
|
84
|
+
for (let col = reference.startCol; col <= reference.endCol; col += 1) {
|
|
85
|
+
cols.add(col);
|
|
86
|
+
}
|
|
87
|
+
target.cellsByRow.set(row, cols);
|
|
88
|
+
}
|
|
89
|
+
targets.set(alias, target);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return targets;
|
|
94
|
+
}
|
|
95
|
+
function extractExternalFormulaReferences(formula) {
|
|
96
|
+
externalReferencePattern.lastIndex = 0;
|
|
97
|
+
return [...formula.matchAll(externalReferencePattern)].flatMap((match) => {
|
|
98
|
+
const bookIndex = Number(match[1] ?? match[4]);
|
|
99
|
+
const sheetName = match[2] ? match[2].replaceAll("''", "'") : (match[5] ?? '');
|
|
100
|
+
const ref = match[3] ?? match[6] ?? '';
|
|
101
|
+
if (!Number.isSafeInteger(bookIndex) || bookIndex < 1 || sheetName.length === 0 || ref.length === 0) {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
const [startRef, endRef] = ref.split(':');
|
|
105
|
+
if (!startRef) {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
let start;
|
|
109
|
+
let end;
|
|
110
|
+
try {
|
|
111
|
+
start = decodeCellAddress(startRef.replaceAll('$', ''));
|
|
112
|
+
end = decodeCellAddress((endRef ?? startRef).replaceAll('$', ''));
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
return [
|
|
118
|
+
{
|
|
119
|
+
bookIndex,
|
|
120
|
+
sheetName,
|
|
121
|
+
startRow: Math.min(start.r, end.r),
|
|
122
|
+
endRow: Math.max(start.r, end.r),
|
|
123
|
+
startCol: Math.min(start.c, end.c),
|
|
124
|
+
endCol: Math.max(start.c, end.c),
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function readExternalLinkReferencesByBookIndex(zip) {
|
|
130
|
+
const workbookXml = getZipText(zip, 'xl/workbook.xml');
|
|
131
|
+
const workbookRelationshipsXml = getZipText(zip, 'xl/_rels/workbook.xml.rels');
|
|
132
|
+
if (!workbookXml || !workbookRelationshipsXml) {
|
|
133
|
+
return new Map();
|
|
134
|
+
}
|
|
135
|
+
const externalLinkTargetsByRelationshipId = new Map();
|
|
136
|
+
for (const match of workbookRelationshipsXml.matchAll(relationshipPattern)) {
|
|
137
|
+
const tag = match[0];
|
|
138
|
+
const id = readXmlAttribute(tag, 'Id');
|
|
139
|
+
const target = readXmlAttribute(tag, 'Target');
|
|
140
|
+
const type = readXmlAttribute(tag, 'Type');
|
|
141
|
+
if (id && target && type && (type === externalLinkRelationshipType || type.endsWith('/externalLink'))) {
|
|
142
|
+
externalLinkTargetsByRelationshipId.set(id, resolveTargetPath('xl/workbook.xml', target));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const references = new Map();
|
|
146
|
+
let bookIndex = 1;
|
|
147
|
+
for (const match of workbookXml.matchAll(/<(?:[A-Za-z_][\w.-]*:)?externalReference\b(?:[^>"']|"[^"]*"|'[^']*')*\/?>/gu)) {
|
|
148
|
+
const relationshipId = readXmlAttribute(match[0], 'r:id') ?? readXmlAttribute(match[0], 'id');
|
|
149
|
+
const path = relationshipId ? externalLinkTargetsByRelationshipId.get(relationshipId) : undefined;
|
|
150
|
+
if (path) {
|
|
151
|
+
const externalLinkXml = getZipText(zip, path);
|
|
152
|
+
const externalTarget = readExternalLinkWorkbookTarget(zip, path);
|
|
153
|
+
const workbookName = workbookIdentityFromName(externalTarget) ?? undefined;
|
|
154
|
+
references.set(bookIndex, {
|
|
155
|
+
bookIndex,
|
|
156
|
+
path,
|
|
157
|
+
...(externalTarget === undefined ? {} : { target: externalTarget }),
|
|
158
|
+
...(workbookName === undefined ? {} : { workbookName }),
|
|
159
|
+
sheetNames: externalLinkXml ? readExternalLinkSheetNames(externalLinkXml) : [],
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
bookIndex += 1;
|
|
163
|
+
}
|
|
164
|
+
return references;
|
|
165
|
+
}
|
|
166
|
+
function parseExternalLinkCachedRows(bookIndex, externalLinkXml, targets) {
|
|
167
|
+
const sheetNames = [...externalLinkXml.matchAll(/<(?:[A-Za-z_][\w.-]*:)?sheetName\b(?:[^>"']|"[^"]*"|'[^']*')*\/?>/gu)].flatMap((match) => {
|
|
168
|
+
const name = readXmlAttribute(match[0], 'val');
|
|
169
|
+
return name ? [name] : [];
|
|
170
|
+
});
|
|
171
|
+
const targetsBySheetName = new Map(targets.map((target) => [target.sheetName, target]));
|
|
172
|
+
const output = new Map();
|
|
173
|
+
for (const match of externalLinkXml.matchAll(/<((?:[A-Za-z_][\w.-]*:)?sheetData)\b((?:[^/>"']|"[^"]*"|'[^']*')*)(?:\/>|>([\s\S]*?)<\/\1>)/gu)) {
|
|
174
|
+
const sheetIdText = readXmlAttribute(match[2] ?? '', 'sheetId');
|
|
175
|
+
const sheetId = sheetIdText === null ? Number.NaN : Number(sheetIdText);
|
|
176
|
+
const sheetName = Number.isSafeInteger(sheetId) && sheetId >= 0 ? sheetNames[sheetId] : undefined;
|
|
177
|
+
const target = sheetName ? targetsBySheetName.get(sheetName) : undefined;
|
|
178
|
+
if (!target || sheetName === undefined) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
const rows = readExternalCachedRows(match[3] ?? '', target.cellsByRow, false);
|
|
182
|
+
if (rows.size > 0) {
|
|
183
|
+
output.set(externalReferenceAlias(bookIndex, sheetName), rows);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return output;
|
|
187
|
+
}
|
|
188
|
+
function readExternalLinkSheetNames(externalLinkXml) {
|
|
189
|
+
return [...externalLinkXml.matchAll(/<(?:[A-Za-z_][\w.-]*:)?sheetName\b(?:[^>"']|"[^"]*"|'[^']*')*\/?>/gu)].flatMap((match) => {
|
|
190
|
+
const name = readXmlAttribute(match[0], 'val');
|
|
191
|
+
return name ? [name] : [];
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
function externalLinkRelationshipPartPath(partPath) {
|
|
195
|
+
const normalized = normalizeZipPath(partPath);
|
|
196
|
+
const fileName = normalized.slice(normalized.lastIndexOf('/') + 1);
|
|
197
|
+
return `xl/externalLinks/_rels/${fileName}.rels`;
|
|
198
|
+
}
|
|
199
|
+
function readExternalLinkWorkbookTarget(zip, externalLinkPath) {
|
|
200
|
+
const relationshipsXml = getZipText(zip, externalLinkRelationshipPartPath(externalLinkPath));
|
|
201
|
+
if (!relationshipsXml) {
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
for (const match of relationshipsXml.matchAll(relationshipPattern)) {
|
|
205
|
+
const tag = match[0];
|
|
206
|
+
const target = readXmlAttribute(tag, 'Target');
|
|
207
|
+
const type = readXmlAttribute(tag, 'Type');
|
|
208
|
+
const targetMode = readXmlAttribute(tag, 'TargetMode');
|
|
209
|
+
if (target && (targetMode === 'External' || type?.endsWith('/externalLinkPath') === true)) {
|
|
210
|
+
return target;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
function readCompanionExternalCachedRowsByAlias(externalWorkbooks, referencesByBookIndex, targetsByBookIndex) {
|
|
216
|
+
const output = new Map();
|
|
217
|
+
const textPatches = [];
|
|
218
|
+
const referenceDiagnostics = [];
|
|
219
|
+
const refreshedBookIndices = [];
|
|
220
|
+
let refreshedSheetCount = 0;
|
|
221
|
+
let refreshedCellCount = 0;
|
|
222
|
+
let skippedNoMatchCount = 0;
|
|
223
|
+
let skippedAmbiguousMatchCount = 0;
|
|
224
|
+
let skippedEmptyRefreshCount = 0;
|
|
225
|
+
for (const [bookIndex, targets] of targetsByBookIndex.entries()) {
|
|
226
|
+
const reference = referencesByBookIndex.get(bookIndex);
|
|
227
|
+
if (!reference) {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const resolved = resolveExternalWorkbookInput(externalWorkbooks, referencesByBookIndex, reference);
|
|
231
|
+
if (!resolved.input) {
|
|
232
|
+
const skippedStatus = resolved.status === 'skipped-no-match' ? 'skipped-no-match' : 'skipped-ambiguous-match';
|
|
233
|
+
if (skippedStatus === 'skipped-no-match') {
|
|
234
|
+
skippedNoMatchCount += 1;
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
skippedAmbiguousMatchCount += 1;
|
|
238
|
+
}
|
|
239
|
+
referenceDiagnostics.push({
|
|
240
|
+
bookIndex,
|
|
241
|
+
...(reference.workbookName === undefined ? {} : { workbookName: reference.workbookName }),
|
|
242
|
+
...(reference.target === undefined ? {} : { target: reference.target }),
|
|
243
|
+
status: skippedStatus,
|
|
244
|
+
candidateCount: resolved.candidateCount,
|
|
245
|
+
...(resolved.referenceCandidateCount === undefined ? {} : { referenceCandidateCount: resolved.referenceCandidateCount }),
|
|
246
|
+
...(resolved.matchKind === undefined ? {} : { matchKind: resolved.matchKind }),
|
|
247
|
+
});
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
const companionRows = readCompanionWorkbookCachedRowsByAlias(resolved.input, bookIndex, targets);
|
|
251
|
+
let referenceSheetCount = 0;
|
|
252
|
+
let referenceCellCount = 0;
|
|
253
|
+
for (const [alias, rows] of companionRows.rowsByAlias.entries()) {
|
|
254
|
+
output.set(alias, rows);
|
|
255
|
+
referenceSheetCount += 1;
|
|
256
|
+
referenceCellCount += countExternalCachedCells(rows);
|
|
257
|
+
}
|
|
258
|
+
if (referenceSheetCount === 0 || referenceCellCount === 0) {
|
|
259
|
+
skippedEmptyRefreshCount += 1;
|
|
260
|
+
referenceDiagnostics.push({
|
|
261
|
+
bookIndex,
|
|
262
|
+
...(reference.workbookName === undefined ? {} : { workbookName: reference.workbookName }),
|
|
263
|
+
...(reference.target === undefined ? {} : { target: reference.target }),
|
|
264
|
+
status: 'skipped-empty-refresh',
|
|
265
|
+
candidateCount: resolved.candidateCount,
|
|
266
|
+
...(resolved.referenceCandidateCount === undefined ? {} : { referenceCandidateCount: resolved.referenceCandidateCount }),
|
|
267
|
+
...(resolved.matchKind === undefined ? {} : { matchKind: resolved.matchKind }),
|
|
268
|
+
...(resolved.input.fileName === undefined ? {} : { matchedFileName: resolved.input.fileName }),
|
|
269
|
+
...(resolved.input.workbookName === undefined ? {} : { matchedWorkbookName: resolved.input.workbookName }),
|
|
270
|
+
...(resolved.input.target === undefined ? {} : { matchedTarget: resolved.input.target }),
|
|
271
|
+
});
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const textPatch = externalLinkCacheTextPatch(reference, companionRows.rowsBySheetName);
|
|
275
|
+
if (textPatch) {
|
|
276
|
+
textPatches.push(textPatch);
|
|
277
|
+
}
|
|
278
|
+
refreshedBookIndices.push(bookIndex);
|
|
279
|
+
refreshedSheetCount += referenceSheetCount;
|
|
280
|
+
refreshedCellCount += referenceCellCount;
|
|
281
|
+
referenceDiagnostics.push({
|
|
282
|
+
bookIndex,
|
|
283
|
+
...(reference.workbookName === undefined ? {} : { workbookName: reference.workbookName }),
|
|
284
|
+
...(reference.target === undefined ? {} : { target: reference.target }),
|
|
285
|
+
status: 'refreshed',
|
|
286
|
+
candidateCount: resolved.candidateCount,
|
|
287
|
+
...(resolved.referenceCandidateCount === undefined ? {} : { referenceCandidateCount: resolved.referenceCandidateCount }),
|
|
288
|
+
...(resolved.matchKind === undefined ? {} : { matchKind: resolved.matchKind }),
|
|
289
|
+
...(resolved.input.fileName === undefined ? {} : { matchedFileName: resolved.input.fileName }),
|
|
290
|
+
...(resolved.input.workbookName === undefined ? {} : { matchedWorkbookName: resolved.input.workbookName }),
|
|
291
|
+
...(resolved.input.target === undefined ? {} : { matchedTarget: resolved.input.target }),
|
|
292
|
+
refreshedSheetCount: referenceSheetCount,
|
|
293
|
+
refreshedCellCount: referenceCellCount,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
const warnings = [
|
|
297
|
+
...(skippedNoMatchCount > 0 ? [externalWorkbookCompanionNoMatchWarning] : []),
|
|
298
|
+
...(skippedAmbiguousMatchCount > 0 ? [externalWorkbookCompanionAmbiguousMatchWarning] : []),
|
|
299
|
+
];
|
|
300
|
+
return {
|
|
301
|
+
rowsByAlias: output,
|
|
302
|
+
textPatches,
|
|
303
|
+
warnings,
|
|
304
|
+
diagnostics: {
|
|
305
|
+
externalWorkbookCount: externalWorkbooks.length,
|
|
306
|
+
externalReferenceCount: targetsByBookIndex.size,
|
|
307
|
+
refreshedBookIndices,
|
|
308
|
+
refreshedSheetCount,
|
|
309
|
+
refreshedCellCount,
|
|
310
|
+
skippedNoMatchCount,
|
|
311
|
+
skippedAmbiguousMatchCount,
|
|
312
|
+
skippedEmptyRefreshCount,
|
|
313
|
+
references: referenceDiagnostics,
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function resolveExternalWorkbookInput(inputs, referencesByBookIndex, reference) {
|
|
318
|
+
const exactTargetCandidates = inputs.filter((input) => externalWorkbookInputMatchesReferenceTarget(input, reference));
|
|
319
|
+
if (exactTargetCandidates.length === 1) {
|
|
320
|
+
return {
|
|
321
|
+
input: exactTargetCandidates[0],
|
|
322
|
+
status: 'matched',
|
|
323
|
+
candidateCount: 1,
|
|
324
|
+
matchKind: 'exact-target',
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
if (exactTargetCandidates.length > 1) {
|
|
328
|
+
return {
|
|
329
|
+
status: 'skipped-ambiguous-match',
|
|
330
|
+
candidateCount: exactTargetCandidates.length,
|
|
331
|
+
matchKind: 'exact-target',
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
const identityCandidates = inputs.filter((input) => externalWorkbookInputMatchesReferenceIdentity(input, reference));
|
|
335
|
+
if (identityCandidates.length === 0) {
|
|
336
|
+
return {
|
|
337
|
+
status: 'skipped-no-match',
|
|
338
|
+
candidateCount: 0,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
const referenceCandidateCount = externalReferenceIdentityCandidateCount(referencesByBookIndex, reference);
|
|
342
|
+
if (identityCandidates.length > 1 || referenceCandidateCount !== 1) {
|
|
343
|
+
return {
|
|
344
|
+
status: 'skipped-ambiguous-match',
|
|
345
|
+
candidateCount: identityCandidates.length,
|
|
346
|
+
referenceCandidateCount,
|
|
347
|
+
matchKind: 'unique-workbook-identity',
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
input: identityCandidates[0],
|
|
352
|
+
status: 'matched',
|
|
353
|
+
candidateCount: 1,
|
|
354
|
+
referenceCandidateCount,
|
|
355
|
+
matchKind: 'unique-workbook-identity',
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function countExternalCachedCells(rows) {
|
|
359
|
+
let count = 0;
|
|
360
|
+
for (const row of rows.values()) {
|
|
361
|
+
count += row.size;
|
|
362
|
+
}
|
|
363
|
+
return count;
|
|
364
|
+
}
|
|
365
|
+
function readCompanionWorkbookCachedRowsByAlias(input, bookIndex, targets) {
|
|
366
|
+
const zip = readXlsxZipEntries(toUint8Array(input.bytes));
|
|
367
|
+
const sheetsByName = new Map(workbookSheetPathEntriesForSource(zip).map((sheet) => [sheetNameKey(sheet.name), sheet]));
|
|
368
|
+
const sharedStrings = readSharedStrings(zip);
|
|
369
|
+
const output = new Map();
|
|
370
|
+
const rowsBySheetName = new Map();
|
|
371
|
+
for (const target of targets) {
|
|
372
|
+
const sheet = sheetsByName.get(sheetNameKey(target.sheetName));
|
|
373
|
+
const sheetXml = sheet ? getZipText(zip, sheet.path) : null;
|
|
374
|
+
if (!sheetXml) {
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
const rows = readExternalCachedRows(sheetXml, target.cellsByRow, true, sharedStrings);
|
|
378
|
+
if (rows.size > 0) {
|
|
379
|
+
output.set(externalReferenceAlias(bookIndex, target.sheetName), rows);
|
|
380
|
+
rowsBySheetName.set(target.sheetName, rows);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return { rowsByAlias: output, rowsBySheetName };
|
|
384
|
+
}
|
|
385
|
+
function externalLinkCacheTextPatch(reference, rowsBySheetName) {
|
|
386
|
+
const rowsBySheetId = new Map();
|
|
387
|
+
for (const [sheetName, rows] of rowsBySheetName.entries()) {
|
|
388
|
+
const sheetId = reference.sheetNames.findIndex((candidate) => sheetNameKey(candidate) === sheetNameKey(sheetName));
|
|
389
|
+
if (sheetId >= 0) {
|
|
390
|
+
rowsBySheetId.set(sheetId, rows);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return rowsBySheetId.size === 0
|
|
394
|
+
? null
|
|
395
|
+
: {
|
|
396
|
+
path: reference.path,
|
|
397
|
+
patchText: (text) => patchExternalLinkCacheXml(text, rowsBySheetId),
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
function patchExternalLinkCacheXml(externalLinkXml, rowsBySheetId) {
|
|
401
|
+
const patchedSheetIds = new Set();
|
|
402
|
+
let nextXml = externalLinkXml.replace(/<((?:[A-Za-z_][\w.-]*:)?sheetData)\b((?:[^/>"']|"[^"]*"|'[^']*')*)(?:\/>|>([\s\S]*?)<\/\1>)/gu, (match, tagName, attributes, body) => {
|
|
403
|
+
const sheetIdText = readXmlAttribute(attributes, 'sheetId');
|
|
404
|
+
const sheetId = sheetIdText === null ? Number.NaN : Number(sheetIdText);
|
|
405
|
+
const rows = Number.isSafeInteger(sheetId) ? rowsBySheetId.get(sheetId) : undefined;
|
|
406
|
+
if (!rows) {
|
|
407
|
+
return match;
|
|
408
|
+
}
|
|
409
|
+
patchedSheetIds.add(sheetId);
|
|
410
|
+
return `<${tagName}${attributes}>${patchExternalSheetDataRows(body ?? '', rows)}</${tagName}>`;
|
|
411
|
+
});
|
|
412
|
+
const missingSheetDataXml = [...rowsBySheetId.entries()]
|
|
413
|
+
.filter(([sheetId]) => !patchedSheetIds.has(sheetId))
|
|
414
|
+
.toSorted((left, right) => left[0] - right[0])
|
|
415
|
+
.map(([sheetId, rows]) => `<sheetData sheetId="${String(sheetId)}">${externalCacheRowsXml(rows)}</sheetData>`)
|
|
416
|
+
.join('');
|
|
417
|
+
if (missingSheetDataXml.length === 0) {
|
|
418
|
+
return nextXml;
|
|
419
|
+
}
|
|
420
|
+
if (/<(?:[A-Za-z_][\w.-]*:)?sheetDataSet\b/u.test(nextXml)) {
|
|
421
|
+
return nextXml.replace(/<\/((?:[A-Za-z_][\w.-]*:)?sheetDataSet)>/u, `${missingSheetDataXml}</$1>`);
|
|
422
|
+
}
|
|
423
|
+
nextXml = nextXml.replace(/<\/((?:[A-Za-z_][\w.-]*:)?externalBook)>/u, `<sheetDataSet>${missingSheetDataXml}</sheetDataSet></$1>`);
|
|
424
|
+
return nextXml;
|
|
425
|
+
}
|
|
426
|
+
function patchExternalSheetDataRows(sheetDataBody, rows) {
|
|
427
|
+
const patchedRows = new Set();
|
|
428
|
+
let nextBody = sheetDataBody.replace(/<((?:[A-Za-z_][\w.-]*:)?row)\b((?:[^>"']|"[^"]*"|'[^']*')*)(?:\/>|>([\s\S]*?)<\/\1>)/gu, (match, tagName, attributes, body) => {
|
|
429
|
+
const rowText = readXmlAttribute(attributes, 'r');
|
|
430
|
+
const rowNumber = rowText === null ? Number.NaN : Number(rowText);
|
|
431
|
+
const row = Number.isSafeInteger(rowNumber) && rowNumber > 0 ? rowNumber - 1 : Number.NaN;
|
|
432
|
+
const cells = Number.isSafeInteger(row) ? rows.get(row) : undefined;
|
|
433
|
+
if (!cells) {
|
|
434
|
+
return match;
|
|
435
|
+
}
|
|
436
|
+
patchedRows.add(row);
|
|
437
|
+
return `<${tagName}${attributes}>${patchExternalRowCells(row, body ?? '', cells)}</${tagName}>`;
|
|
438
|
+
});
|
|
439
|
+
for (const [row, cells] of [...rows.entries()].toSorted((left, right) => left[0] - right[0])) {
|
|
440
|
+
if (!patchedRows.has(row)) {
|
|
441
|
+
nextBody = insertExternalCacheRowXml(nextBody, row, cells);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return nextBody;
|
|
445
|
+
}
|
|
446
|
+
function patchExternalRowCells(row, rowBody, cells) {
|
|
447
|
+
const patchedCols = new Set();
|
|
448
|
+
let nextBody = rowBody.replace(/<((?:[A-Za-z_][\w.-]*:)?(?:cell|c))\b((?:[^>"']|"[^"]*"|'[^']*')*)(?:\/>|>([\s\S]*?)<\/\1>)/gu, (match, _tagName, attributes) => {
|
|
449
|
+
const addressText = readXmlAttribute(attributes, 'r');
|
|
450
|
+
let address;
|
|
451
|
+
try {
|
|
452
|
+
address = addressText ? decodeCellAddress(addressText.replaceAll('$', '')) : undefined;
|
|
453
|
+
}
|
|
454
|
+
catch {
|
|
455
|
+
address = undefined;
|
|
456
|
+
}
|
|
457
|
+
if (!address) {
|
|
458
|
+
return match;
|
|
459
|
+
}
|
|
460
|
+
const value = cells.get(address.c);
|
|
461
|
+
if (value === undefined) {
|
|
462
|
+
return match;
|
|
463
|
+
}
|
|
464
|
+
patchedCols.add(address.c);
|
|
465
|
+
return externalCacheCellXml(row, address.c, value);
|
|
466
|
+
});
|
|
467
|
+
for (const [col, value] of [...cells.entries()].toSorted((left, right) => left[0] - right[0])) {
|
|
468
|
+
if (!patchedCols.has(col)) {
|
|
469
|
+
nextBody = insertExternalCacheCellXml(nextBody, row, col, value);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return nextBody;
|
|
473
|
+
}
|
|
474
|
+
function insertExternalCacheRowXml(sheetDataBody, row, cells) {
|
|
475
|
+
const rowXml = `<row r="${String(row + 1)}">${externalCacheCellsXml(row, cells)}</row>`;
|
|
476
|
+
for (const match of sheetDataBody.matchAll(/<((?:[A-Za-z_][\w.-]*:)?row)\b((?:[^>"']|"[^"]*"|'[^']*')*)(?:\/>|>[\s\S]*?<\/\1>)/gu)) {
|
|
477
|
+
const rowText = readXmlAttribute(match[2] ?? '', 'r');
|
|
478
|
+
const rowNumber = rowText === null ? Number.NaN : Number(rowText);
|
|
479
|
+
if (Number.isSafeInteger(rowNumber) && rowNumber > row + 1) {
|
|
480
|
+
return `${sheetDataBody.slice(0, match.index)}${rowXml}${sheetDataBody.slice(match.index)}`;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return `${sheetDataBody}${rowXml}`;
|
|
484
|
+
}
|
|
485
|
+
function insertExternalCacheCellXml(rowBody, row, col, value) {
|
|
486
|
+
const cellXml = externalCacheCellXml(row, col, value);
|
|
487
|
+
for (const match of rowBody.matchAll(/<((?:[A-Za-z_][\w.-]*:)?(?:cell|c))\b((?:[^>"']|"[^"]*"|'[^']*')*)(?:\/>|>[\s\S]*?<\/\1>)/gu)) {
|
|
488
|
+
const addressText = readXmlAttribute(match[2] ?? '', 'r');
|
|
489
|
+
let address;
|
|
490
|
+
try {
|
|
491
|
+
address = addressText ? decodeCellAddress(addressText.replaceAll('$', '')) : undefined;
|
|
492
|
+
}
|
|
493
|
+
catch {
|
|
494
|
+
address = undefined;
|
|
495
|
+
}
|
|
496
|
+
if (address && address.c > col) {
|
|
497
|
+
return `${rowBody.slice(0, match.index)}${cellXml}${rowBody.slice(match.index)}`;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return `${rowBody}${cellXml}`;
|
|
501
|
+
}
|
|
502
|
+
function externalCacheRowsXml(rows) {
|
|
503
|
+
return [...rows.entries()]
|
|
504
|
+
.toSorted((left, right) => left[0] - right[0])
|
|
505
|
+
.map(([row, cells]) => `<row r="${String(row + 1)}">${externalCacheCellsXml(row, cells)}</row>`)
|
|
506
|
+
.join('');
|
|
507
|
+
}
|
|
508
|
+
function externalCacheCellsXml(row, cells) {
|
|
509
|
+
return [...cells.entries()]
|
|
510
|
+
.toSorted((left, right) => left[0] - right[0])
|
|
511
|
+
.map(([col, value]) => externalCacheCellXml(row, col, value))
|
|
512
|
+
.join('');
|
|
513
|
+
}
|
|
514
|
+
function externalCacheCellXml(row, col, value) {
|
|
515
|
+
const address = escapeXmlAttribute(encodeCellAddress({ r: row, c: col }));
|
|
516
|
+
if ('kind' in value) {
|
|
517
|
+
return `<cell r="${address}"/>`;
|
|
518
|
+
}
|
|
519
|
+
switch (value.tag) {
|
|
520
|
+
case ValueTag.Empty:
|
|
521
|
+
return `<cell r="${address}"/>`;
|
|
522
|
+
case ValueTag.Number:
|
|
523
|
+
return Number.isFinite(value.value) ? `<cell r="${address}"><v>${String(value.value)}</v></cell>` : `<cell r="${address}"/>`;
|
|
524
|
+
case ValueTag.Boolean:
|
|
525
|
+
return `<cell r="${address}" t="b"><v>${value.value ? '1' : '0'}</v></cell>`;
|
|
526
|
+
case ValueTag.String:
|
|
527
|
+
return `<cell r="${address}" t="str"><v>${escapeXmlText(value.value)}</v></cell>`;
|
|
528
|
+
case ValueTag.Error:
|
|
529
|
+
return `<cell r="${address}" t="e"><v>${escapeXmlText(formatErrorCode(value.code))}</v></cell>`;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
function readSharedStrings(zip) {
|
|
533
|
+
const sharedStringsXml = getZipText(zip, 'xl/sharedStrings.xml');
|
|
534
|
+
if (!sharedStringsXml) {
|
|
535
|
+
return [];
|
|
536
|
+
}
|
|
537
|
+
return [...sharedStringsXml.matchAll(/<((?:[A-Za-z_][\w.-]*:)?si)\b(?:[^>"']|"[^"]*"|'[^']*')*>([\s\S]*?)<\/\1>/gu)].map((match) => readTextRuns(match[2] ?? ''));
|
|
538
|
+
}
|
|
539
|
+
function readExternalCachedRows(xml, targetCellsByRow, fillMissingCells, sharedStrings = []) {
|
|
540
|
+
const output = new Map();
|
|
541
|
+
if (fillMissingCells) {
|
|
542
|
+
for (const [row, cols] of targetCellsByRow.entries()) {
|
|
543
|
+
output.set(row, new Map([...cols].map((col) => [col, emptyCellValue])));
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
for (const rowMatch of xml.matchAll(/<((?:[A-Za-z_][\w.-]*:)?row)\b((?:[^>"']|"[^"]*"|'[^']*')*)>([\s\S]*?)<\/\1>/gu)) {
|
|
547
|
+
const rowText = readXmlAttribute(rowMatch[2] ?? '', 'r');
|
|
548
|
+
const rowNumber = rowText === null ? Number.NaN : Number(rowText);
|
|
549
|
+
if (!Number.isSafeInteger(rowNumber) || rowNumber < 1) {
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
const row = rowNumber - 1;
|
|
553
|
+
const targetCols = targetCellsByRow.get(row);
|
|
554
|
+
if (!targetCols) {
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
const rowValues = output.get(row) ?? new Map();
|
|
558
|
+
for (const cellMatch of (rowMatch[3] ?? '').matchAll(/<((?:[A-Za-z_][\w.-]*:)?(?:cell|c))\b(?:[^>"']|"[^"]*"|'[^']*')*\/>|<((?:[A-Za-z_][\w.-]*:)?(?:cell|c))\b(?:[^>"']|"[^"]*"|'[^']*')*>[\s\S]*?<\/\2>/gu)) {
|
|
559
|
+
const cellXml = cellMatch[0];
|
|
560
|
+
const openingTag = /<(?:[A-Za-z_][\w.-]*:)?(?:cell|c)\b(?:[^>"']|"[^"]*"|'[^']*')*(?:\/>|>)/u.exec(cellXml)?.[0];
|
|
561
|
+
const addressText = openingTag ? readXmlAttribute(openingTag, 'r') : null;
|
|
562
|
+
if (!openingTag || !addressText) {
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
let address;
|
|
566
|
+
try {
|
|
567
|
+
address = decodeCellAddress(addressText.replaceAll('$', ''));
|
|
568
|
+
}
|
|
569
|
+
catch {
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
if (!targetCols.has(address.c)) {
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
rowValues.set(address.c, readExternalCachedCellValue(cellXml, openingTag, sharedStrings));
|
|
576
|
+
}
|
|
577
|
+
output.set(row, rowValues);
|
|
578
|
+
}
|
|
579
|
+
return output;
|
|
580
|
+
}
|
|
581
|
+
function readExternalCachedCellValue(cellXml, openingTag, sharedStrings = []) {
|
|
582
|
+
const type = readXmlAttribute(openingTag, 't');
|
|
583
|
+
if (type === 'inlineStr') {
|
|
584
|
+
return stringCellValue(readTextRuns(cellXml));
|
|
585
|
+
}
|
|
586
|
+
const rawValue = readElementText(cellXml, 'v');
|
|
587
|
+
if (rawValue === null) {
|
|
588
|
+
return emptyCellValue;
|
|
589
|
+
}
|
|
590
|
+
if (type === 'str') {
|
|
591
|
+
return stringCellValue(decodeXmlText(rawValue));
|
|
592
|
+
}
|
|
593
|
+
if (type === 's') {
|
|
594
|
+
const index = Number(decodeXmlText(rawValue).trim());
|
|
595
|
+
const value = Number.isSafeInteger(index) && index >= 0 ? sharedStrings[index] : undefined;
|
|
596
|
+
return value === undefined ? emptyCellValue : stringCellValue(value);
|
|
597
|
+
}
|
|
598
|
+
if (type === 'b') {
|
|
599
|
+
return { tag: ValueTag.Boolean, value: rawValue === '1' || rawValue.toLowerCase() === 'true' };
|
|
600
|
+
}
|
|
601
|
+
if (type === 'e') {
|
|
602
|
+
return { tag: ValueTag.Error, code: errorCodeForText(decodeXmlText(rawValue)) };
|
|
603
|
+
}
|
|
604
|
+
const numeric = Number(rawValue);
|
|
605
|
+
return Number.isFinite(numeric) ? { tag: ValueTag.Number, value: numeric } : stringCellValue(decodeXmlText(rawValue));
|
|
606
|
+
}
|
|
607
|
+
function toUint8Array(bytes) {
|
|
608
|
+
return bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
|
|
609
|
+
}
|
|
610
|
+
function normalizedWorkbookTarget(value) {
|
|
611
|
+
if (!value) {
|
|
612
|
+
return null;
|
|
613
|
+
}
|
|
614
|
+
const withoutFragment = value.split('#')[0]?.trim();
|
|
615
|
+
if (!withoutFragment) {
|
|
616
|
+
return null;
|
|
617
|
+
}
|
|
618
|
+
const normalized = withoutFragment.replace(/\\/gu, '/').replace(/^file:\/+/iu, '/');
|
|
619
|
+
try {
|
|
620
|
+
return decodeURIComponent(normalized).toLocaleLowerCase('en-US');
|
|
621
|
+
}
|
|
622
|
+
catch {
|
|
623
|
+
return normalized.toLocaleLowerCase('en-US');
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
function workbookIdentityFromName(value) {
|
|
627
|
+
const target = normalizedWorkbookTarget(value);
|
|
628
|
+
if (!target) {
|
|
629
|
+
return null;
|
|
630
|
+
}
|
|
631
|
+
const segments = target.split('/').filter((segment) => segment.length > 0);
|
|
632
|
+
return segments.at(-1) ?? target;
|
|
633
|
+
}
|
|
634
|
+
function externalWorkbookInputIdentityNames(input) {
|
|
635
|
+
return new Set([input.workbookName, input.fileName, input.target]
|
|
636
|
+
.map(workbookIdentityFromName)
|
|
637
|
+
.filter((value) => value !== null && value.length > 0));
|
|
638
|
+
}
|
|
639
|
+
function externalReferenceIdentityNames(reference) {
|
|
640
|
+
return new Set([reference.workbookName, reference.target]
|
|
641
|
+
.map(workbookIdentityFromName)
|
|
642
|
+
.filter((value) => value !== null && value.length > 0));
|
|
643
|
+
}
|
|
644
|
+
function externalWorkbookInputMatchesReferenceTarget(input, reference) {
|
|
645
|
+
const inputTarget = normalizedWorkbookTarget(input.target);
|
|
646
|
+
const referenceTarget = normalizedWorkbookTarget(reference.target);
|
|
647
|
+
return Boolean(inputTarget && referenceTarget && inputTarget === referenceTarget);
|
|
648
|
+
}
|
|
649
|
+
function externalWorkbookInputMatchesReferenceIdentity(input, reference) {
|
|
650
|
+
const inputTarget = normalizedWorkbookTarget(input.target);
|
|
651
|
+
const referenceTarget = normalizedWorkbookTarget(reference.target);
|
|
652
|
+
if (inputTarget && referenceTarget) {
|
|
653
|
+
return false;
|
|
654
|
+
}
|
|
655
|
+
const inputNames = externalWorkbookInputIdentityNames(input);
|
|
656
|
+
if (inputNames.size === 0) {
|
|
657
|
+
return false;
|
|
658
|
+
}
|
|
659
|
+
for (const referenceName of externalReferenceIdentityNames(reference)) {
|
|
660
|
+
if (inputNames.has(referenceName)) {
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
return false;
|
|
665
|
+
}
|
|
666
|
+
function externalReferenceIdentityCandidateCount(referencesByBookIndex, reference) {
|
|
667
|
+
const referenceNames = externalReferenceIdentityNames(reference);
|
|
668
|
+
if (referenceNames.size === 0) {
|
|
669
|
+
return 0;
|
|
670
|
+
}
|
|
671
|
+
let count = 0;
|
|
672
|
+
for (const candidate of referencesByBookIndex.values()) {
|
|
673
|
+
for (const referenceName of externalReferenceIdentityNames(candidate)) {
|
|
674
|
+
if (referenceNames.has(referenceName)) {
|
|
675
|
+
count += 1;
|
|
676
|
+
break;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return count;
|
|
681
|
+
}
|
|
682
|
+
function resolveTargetPath(fromPath, target) {
|
|
683
|
+
if (target.startsWith('/')) {
|
|
684
|
+
return normalizeZipPath(target);
|
|
685
|
+
}
|
|
686
|
+
const baseParts = normalizeZipPath(fromPath).split('/').slice(0, -1);
|
|
687
|
+
for (const part of target.split('/')) {
|
|
688
|
+
if (part.length === 0 || part === '.') {
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
if (part === '..') {
|
|
692
|
+
baseParts.pop();
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
baseParts.push(part);
|
|
696
|
+
}
|
|
697
|
+
return normalizeZipPath(baseParts.join('/'));
|
|
698
|
+
}
|
|
699
|
+
function quoteFormulaSheetName(sheetName) {
|
|
700
|
+
return `'${sheetName.replaceAll("'", "''")}'`;
|
|
701
|
+
}
|
|
702
|
+
function externalReferenceAlias(bookIndex, sheetName) {
|
|
703
|
+
let encodedSheetName = '';
|
|
704
|
+
for (const character of sheetName) {
|
|
705
|
+
encodedSheetName += /^[A-Za-z0-9_]$/u.test(character) ? character : `_u${character.codePointAt(0).toString(16).padStart(4, '0')}_`;
|
|
706
|
+
}
|
|
707
|
+
return `__bilig_external_${String(bookIndex)}_${encodedSheetName}`;
|
|
708
|
+
}
|
|
709
|
+
function sheetNameKey(sheetName) {
|
|
710
|
+
return sheetName.toLocaleLowerCase('en-US');
|
|
711
|
+
}
|
|
712
|
+
function stringCellValue(value) {
|
|
713
|
+
return { tag: ValueTag.String, value, stringId: 0 };
|
|
714
|
+
}
|
|
715
|
+
function errorCodeForText(value) {
|
|
716
|
+
switch (value.toUpperCase()) {
|
|
717
|
+
case '#DIV/0!':
|
|
718
|
+
return ErrorCode.Div0;
|
|
719
|
+
case '#REF!':
|
|
720
|
+
return ErrorCode.Ref;
|
|
721
|
+
case '#VALUE!':
|
|
722
|
+
return ErrorCode.Value;
|
|
723
|
+
case '#NAME?':
|
|
724
|
+
return ErrorCode.Name;
|
|
725
|
+
case '#N/A':
|
|
726
|
+
return ErrorCode.NA;
|
|
727
|
+
case '#NUM!':
|
|
728
|
+
return ErrorCode.Num;
|
|
729
|
+
case '#FIELD!':
|
|
730
|
+
return ErrorCode.Field;
|
|
731
|
+
case '#NULL!':
|
|
732
|
+
return ErrorCode.Null;
|
|
733
|
+
default:
|
|
734
|
+
return ErrorCode.Value;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
function readTextRuns(xml) {
|
|
738
|
+
const runs = [...xml.matchAll(/<(?:[A-Za-z_][\w.-]*:)?t\b(?:[^>"']|"[^"]*"|'[^']*')*>([\s\S]*?)<\/(?:[A-Za-z_][\w.-]*:)?t>/gu)].map((match) => decodeXmlText(match[1] ?? ''));
|
|
739
|
+
return runs.length > 0 ? runs.join('') : '';
|
|
740
|
+
}
|
|
741
|
+
function readElementText(xml, name) {
|
|
742
|
+
const escapedName = name.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
743
|
+
return (new RegExp(`<(?:[A-Za-z_][\\w.-]*:)?${escapedName}\\b(?:[^>"']|"[^"]*"|'[^']*')*>([\\s\\S]*?)<\\/(?:[A-Za-z_][\\w.-]*:)?${escapedName}>`, 'u').exec(xml)?.[1] ?? null);
|
|
744
|
+
}
|
|
745
|
+
function decodeXmlText(value) {
|
|
746
|
+
return value
|
|
747
|
+
.replace(/"/gu, '"')
|
|
748
|
+
.replace(/'/gu, "'")
|
|
749
|
+
.replace(/</gu, '<')
|
|
750
|
+
.replace(/>/gu, '>')
|
|
751
|
+
.replace(/&/gu, '&');
|
|
752
|
+
}
|
|
753
|
+
//# sourceMappingURL=streaming-native-external-cache.js.map
|